【问题标题】:How can I SELECT distinct data based on a date field?如何根据日期字段选择不同的数据?
【发布时间】:2011-09-02 03:24:09
【问题描述】:

我有一个表,它在另一个表中存储了对对象的更改日志。这是我的表格内容:

ObjID   Color   Date                     User
------- ------- ------------------------ --------
1       Red     2010-01-01 12:22:00.000  Joe
1       Blue    2010-01-02 15:22:00.000  Jill
1       Green   2010-01-03 16:22:00.000  Joe
1       White   2010-01-10 09:22:00.000  Mike
2       Red     2010-01-09 10:22:00.000  Mike
2       Blue    2010-01-12 09:22:00.000  Jill
2       Orange  2010-01-12 15:22:00.000  Joe

我想为每个对象选择最近的日期,以及该记录日期的颜色和用户。

基本上,我想要这个结果集:

ObjID   Color   Date                     User
------- ------- ------------------------ --------
1       White   2010-01-10 09:22:00.000  Mike
2       Orange  2010-01-12 15:22:00.000  Joe

我无法理解为获取此数据而需要编写的 SQL 查询...

我正在通过 ODBC 从 iSeries DB2 数据库 (AS/400) 检索数据。

【问题讨论】:

  • 那张桌子上你也有身份Pk吗?

标签: sql tsql select group-by greatest-n-per-group


【解决方案1】:

您好,我认为您需要以下内容(其中 ColorTable 是您的表名):

SELECT Color.* 
FROM ColorTable as Color
INNER JOIN 
(
SELECT ObjID, MAX(Date) as Date
FROM ColorTable
GROUP BY ObjID
) as MaxDateByColor
ON Color.ObjID = MaxDateByColor.ObjID
AND Color.Date = MaxDateByColor.Date 

【讨论】:

  • 所以基本上,您在子选择中获得每种颜色的最大日期,然后将这些不同的组合加入到整个表中,在最终结果中提供整个行结果。
  • 大声笑,我的生活故事。我想有人告诉我,当我在高中竞选学生会时:P
  • 啊!谢谢马丁。我试图回复这么快,我没有注意到他的标准是对象,而不是颜色。 >.
【解决方案2】:

假设至少 SQL Server 2005

DECLARE @T TABLE (ObjID INT,Color VARCHAR(10),[Date] DATETIME,[User] VARCHAR(50))

INSERT INTO @T
SELECT 1,'Red',' 2010-01-01 12:22:00.000','Joe' UNION ALL
SELECT 1,'Blue','2010-01-02 15:22:00.000','Jill' UNION ALL
SELECT 1,'Green',' 2010-01-03 16:22:00.000','Joe' UNION ALL
SELECT 1,'White',' 2010-01-10 09:22:00.000','Mike' UNION ALL
SELECT 2,'Red',' 2010-01-09 10:22:00.000','Mike' UNION ALL
SELECT 2,'Blue','2010-01-12 09:22:00.000','Jill' UNION ALL
SELECT 2,'Orange','2010-01-12 15:22:00.000','Joe'

;WITH T AS
(
SELECT *,
       ROW_NUMBER() OVER (PARTITION BY ObjID ORDER BY Date DESC) AS RN
FROM @T
)
SELECT ObjID,
       Color,
       [Date],
       [User]
FROM T 
WHERE RN=1

或者来自 cmets 中链接的文章中的 SQL Server 2000 方法

SELECT ObjID,
  CAST(SUBSTRING(string, 24, 33) AS VARCHAR(10)) AS Color,
  CAST(SUBSTRING(string,  1, 23) AS DATETIME ) AS [Date],
  CAST(SUBSTRING(string, 34, 83) AS  VARCHAR(50)) AS [User]
FROM 
(
SELECT ObjID, 
          MAX((CONVERT(CHAR(23), [Date], 126)
         + CAST(Color AS CHAR(10))
         + CAST([User] AS CHAR(50))) COLLATE Latin1_General_BIN) AS string
FROM @T
GROUP BY ObjID) T;

【讨论】:

  • 如果同一 objID 上的两个日期相同,这是唯一有效的方法。但根据表的大小,它可能不是最佳的。
  • @Magnus - 这里有一些关于性能的信息sqlmag.com/article/departments/…
  • 更好的是在 Obj1 + 日期上添加唯一索引。日期列应该有足够的精度。或者有一个 int identity pk 列,然后在上面做 max (按 ObjID 分组)
【解决方案3】:

如果您有一个 Objects 表,并且您的 ObjectHistory 表在 ObjID 和日期上有一个索引,那么这可能比目前给出的其他查询执行得更好:

SELECT
   X.*
FROM
   Objects O
   CROSS APPLY (
      SELECT TOP 1 *
      FROM ObjectHistory H
      WHERE O.ObjID = O.ObjID
      ORDER BY H.[Date] DESC
   ) X

只有从 Objects 表中提取列时,性能改进才会出现,但值得一试。

如果您想要所有对象,无论它们是否有历史条目,请切换到OUTER APPLY(当然使用O.ObjID 而不是H.ObjID)。

这个查询的巧妙之处在于

  1. 它解决了日期值可能有重复的情况
  2. 它可以支持每组任意数量的项目(例如,前 5 个而不是前 1 个)

【讨论】:

  • Date(time)Changed不能有重复,IIUC SQL并发。
【解决方案4】:
【解决方案5】:
SELECT t1.* FROM Table_name as t1
INNER JOIN (
  SELECT MAX(Date) as MaxDate, ObjID FROM Table_name
  GROUP BY ObjID
) as t2
ON t1.ObjID = t2.ObjID AND t1.Date = t2.MaxDate

【讨论】:

    【解决方案6】:

    您可以像这样查看每个对象的最新更改:

            select objectid, max(changedate) as LatestChange
            from LOG
            group by objectid
    

    然后,您可以通过将上面返回的集合(实例化为已赋予别名的内联视图)再次链接到同一个表来获取颜色和用户列:

           select color, user, FOO.objectid, FOO.LatestChange
           from LOG
           inner join
           (
    
              select objectid, max(changedate) as LatestChange
            from LOG
            group by objectid
    
    
            ) as FOO
            on LOG.objectid = FOO.objectid and LOG.changedate = FOO.LatestChange
    

    【讨论】:

      【解决方案7】:

      就像上面的马丁史密斯一样, 只需在分区上做一个行号并选择最近的行之一 喜欢

      SELECT  Color,Date,User
      FROM (
         SELECT *,
                ROW_NUMBER() OVER (PARTITION BY User ORDER BY [DATE]) AS ROW_NUMBER
         FROM [tablename]
         ) AS ROWS
      WHERE 
      ROW_NUMBER = 2
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-14
        • 1970-01-01
        • 1970-01-01
        • 2015-08-21
        • 1970-01-01
        • 1970-01-01
        • 2014-09-22
        • 2014-05-11
        相关资源
        最近更新 更多