选择列表中值的第一个外观(DISTINCT / GROUP BY)

时间:2014-07-24 17:08:57

标签: php mysql sql sql-server group-by

我有一个查询,它使用两个JOIN,以这种格式返回一个列表:

unique_id | non_unique_id | timestamp

完整列表很大(数千行),查询结果只有几十行,因为查询的 WHERE时间戳> ='过去的某个时间戳'

所以现在,我有这样的清单:

89 | 286 | 1406219705
87 | 286 | 1406219518
79 | 922 | 1406216949
78 | 228 | 1406216871
77 | 126 | 1406216748
76 | 939 | 1406216722
74 | 126 | 1406216352
64 | 939 | 1406212540
63 | 126 | 1406212522
49 | 228 | 1406205715
48 | 228 | 1406204851
37 | 228 | 1406196435
32 | 228 | 1406190209
23 | 126 | 1406182577  <- 'limiting timestamp'
18 | 871 | 1406181991
10 | 922 | 1406178816
 9 | 764 | 1406178778
 7 | 609 | 1406178699
 5 | 126 | 1406177398
 4 | 871 | 1406177379  <- 'some timestamp in the past'

现在,我只需要在'限制时间戳'和列表末尾('过去的某个时间戳')之间选择行。我可以在原始查询的WHERE条件中指定'限制时间戳',但问题是:我需要结果集没有 non_unique_id 的记录,已经出现在'限制时间戳'上方的列表中。这是查询结果的样子:

                       <- 'limiting timestamp'
18 | 871 | 1406181991

 9 | 764 | 1406178778
 7 | 609 | 1406178699

                       <- 'some timestamp in the past'

因此,结果将返回3行,这些行都具有 non_unique_id ,这些行未出现在上面的结果中。但是如果'non_unique_id'已经出现在'限制时间戳''过去'中的某个时间戳之间的列表中,则只应保留第一次出现。注意:最后一部分条件是可选的,因为从最终列表中提取副本非常容易。

到目前为止,我只能提出在列表&gt; ='过去的某个时间戳&gt;之间进行联接的解决方案。 '限制时间戳'。这样我就会看到底部列表中是否出现过顶部列表。但是,可以假设查询很复杂,生成结果所需的时间不应该再次运行加倍,但条件略有不同。

2 个答案:

答案 0 :(得分:1)

试试这个:

SELECT *
FROM my_table AS t1
WHERE timestamp < @limiting_timestamp
  AND timestamp > @some_timestamp_in_the_past
  AND NOT EXISTS(SELECT 1
              FROM my_table AS t2
              WHERE timestamp > @limiting_timestamp
                AND t1.non_unique_id = t2.non_unique_id)

这将为您提供在限制时间戳之后未出现的时间戳之间的记录。请注意,如果您希望记录等于过滤时间戳,则可以使用between关键字而不是&lt;和&gt;。

要消除你可以使用另一个子查询:

AND t1.timestamp = (SELECT MAX(timestamp)
                FROM my_table AS t2
                WHERE timestamp < @limiting_timestamp
                  AND timestamp > @some_timestamp_in_the_past
                GROUP BY unique_id)

答案 1 :(得分:1)

如果SQL Server 2008+(sqlfiddle:http://sqlfiddle.com/#!3/0bc33/3):

,您可以尝试此操作
WITH cteOrdered
AS
(
    SELECT    ROW_NUMBER() OVER (PARTITION BY t1.Non_Unique_ID ORDER BY t1.Timestamp) AS RID,
              t1.*
    FROM      Table1 t1 LEFT JOIN
              (SELECT  Non_Unique_ID
               FROM    Table1
               WHERE   Timestamp < 1406177379 OR
                       Timestamp > 1406182577) t2
              ON t1.Non_Unique_ID = t2.Non_Unique_ID
    WHERE     t2.Non_Unique_ID IS NULL AND
              t1.Timestamp > 1406177379 AND 
              t1.Timestamp < 1406182577
 )

SELECT    Unique_ID,
          Non_Unique_ID,
          Timestamp
FROM      cteOrdered
WHERE     RID = 1;

我在数据中添加了另一行

(18, 871, 1406181990),

查看查询是否产生了您想要的内容。您说如果搜索范围内存在重复的non_unique_id,则只应保留“第一个”事件。我认为这是具有EARLIEST时间戳的那个?如果相反,您可以更改此行

SELECT    ROW_NUMBER() OVER (PARTITION BY t1.Non_Unique_ID ORDER BY t1.Timestamp) AS RID,

SELECT    ROW_NUMBER() OVER (PARTITION BY t1.Non_Unique_ID ORDER BY t1.Timestamp DESC) AS RID,

这将翻转顺序以保留重复项的最新时间戳。