【问题标题】:Selecting specific rows from a table, using a group by query使用 group by 查询从表中选择特定行
【发布时间】:2021-10-27 02:43:30
【问题描述】:

我有一个看起来像这样的表:

+------------+------------+--------------+
| Date       | Name       | Certificates |
+------------+------------+--------------+
| 2021-02-01 | Jason      | 3            |
| 2021-02-01 | Nisha      | 4            |
| 2021-02-01 | Zaid       | 5            |
| 2021-03-25 | Aniket     | 4            |
| 2021-03-25 | Anish      | 2            |
| 2021-03-25 | Nadia      | 0            |
| 2021-05-06 | Aadil      | 7            |
| 2021-05-06 | Ashish     | 1            |
| 2021-05-06 | Rahil      | 9            |
+------------+------------+--------------+

此结果是通过执行以下 SQL 查询获得的:

SELECT 
    Date, Name, COUNT(Certificates) as Certificates
FROM Students.data
GROUP BY Date, Name
ORDER BY Date, Name;

收到这个结果后,理想情况下,我现在只想要每个日期的第一个条目(基本上是每个日期的名字),应该是这样的:

+------------+------------+--------------+
| Date       | Name       | Certificates |
+------------+------------+--------------+
| 2021-02-01 | Jason      | 3            |
| 2021-03-25 | Aniket     | 4            |
| 2021-05-06 | Aadil      | 7            |
+------------+------------+--------------+

有没有办法可以通过查询修改上述组以获得结果,或者我是否需要将此查询的结果传递给其他查询,如果是,该查询将是什么。 谢谢。

另外,我使用的数据库是 Clickhouse。

注意:如果问题有任何问题,请告诉我,可以澄清一下。

【问题讨论】:

  • 您需要在这里定义每个日期的“第一条”记录的实际含义。从你的问题看不清楚。
  • 嘿@TimBiegeleisen 我已经进行了编辑,希望现在更有意义。
  • @ShaikhAbuzar 你预计最终输出是 3,4,7 还是证书列中的所有这些都是 1
  • @ShaikhAbuzar 检查下面的结果dbfiddle.uk/… 是你想要的吗?

标签: sql clickhouse


【解决方案1】:

您认为您的结果是一个中间结果,您希望每个日期从中选择一行。为此,您可以使用ROW_NUMBER 按名称对每个日期的行进行编号,并且只保留日期的第一行(那些编号为 1 的行)。

SELECT date, name, certificates
FROM
(
  SELECT 
    date, name, COUNT(Certificates) AS certificates,
    ROW_NUMBER() OVER (PARTITION BY date ORDER BY name) AS rn
  FROM students.data
  GROUP BY date, name
) numbered
WHERE rn = 1
ORDER BY date;

Demo:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=93c3682bda72cb4fe53fbbe8053a8acb(这里使用 MySQL 8,因为 dbfiddle.uk 没有 clickhouse,但查询是标准 SQL 兼容的,所以我们可以使用几乎所有现代 RDBMS 进行演示)。

【讨论】:

  • 这个问题的措辞很糟糕......让我觉得也许对 OP 方法的解释也被过度考虑了。这看起来是正确的 +1。
  • 如果您按 rank = 1 过滤,则不需要执行 count()
  • @trillion:你错了。再次查看请求。那里显示的表是按日期和名称聚合数据表并计算其行数的结果。没有COUNT,我们就得不到证书的数量。
  • @ThorstenKettner 您的代码仅针对证书列返回 1,这是预期的吗?因为他上面显示的输出在证书列中有 3,4,7
  • @ThorstenKettner 这里是您查询的小提琴:dbfiddle.uk/…,我相信他需要的是:dbfiddle.uk/…
【解决方案2】:
  • 使用CTE 代替子查询
  • 对数据进行排序 => 每一行都有相同的数据,并且排名不断增加 --> ROW_NUMBER
  • 按 1 过滤 rank_ 以获得每个日期一个条目
  • 假设您需要什么,则按名称按字母顺序排序

如果您还没有计数,请使用 code 1 小提琴:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a5a8bf3f6934f18b19d331d3ba43570a

withranked_data AS ( 选择日期_,姓名, count(certificates_) over(partition by date_,name) 作为证书, row_number() OVER(PARTITION BY date_ order by name) as rank_ 来自学生 ) 选择 日期_,名称,来​​自ranked_data的证书WHERE rank_ = 1

如果您有计数,请使用 代码 2


WITH ranked_data AS (
SELECT date_, name, certificates_,
row_number() OVER(PARTITION BY date_ order by name) as rank_
FROM students
)
SELECT 
  date_, name, certificates_ FROM ranked_data WHERE rank_ = 1

【讨论】:

    【解决方案3】:
    • 直截了当
    SELECT Date, untuple(groupArray(tuple(Name, Certificates))[1])
    FROM (
        SELECT *
        FROM  (
            /* Emulate the test dataset. */
            SELECT toDate(data.1) AS Date, data.2 AS Name, data.3 AS Certificates
            FROM (
                SELECT arrayJoin([
                    ('2021-02-01', 'Jason ', 3),
                    ('2021-02-01', 'Nisha ', 4),
                    ('2021-02-01', 'Zaid  ', 5),
                    ('2021-03-25', 'Aniket', 4),
                    ('2021-03-25', 'Anish ', 2),
                    ('2021-03-25', 'Nadia ', 0),
                    ('2021-05-06', 'Aadil ', 7),
                    ('2021-05-06', 'Ashish', 1),
                    ('2021-05-06', 'Rahil ', 9)]) AS data
                )
            )
        ORDER BY Date, Name
        )
    GROUP BY Date
    
    /*
    ┌───────Date─┬─Name───┬─Certificates─┐
    │ 2021-02-01 │ Jason  │            3 │
    │ 2021-03-25 │ Aniket │            4 │
    │ 2021-05-06 │ Aadil  │            7 │
    └────────────┴────────┴──────────────┘
    */
    
    • 基于窗口函数的方式

    version 21.4开始添加了对窗口函数的完整支持。此时它被标记为实验性功能

    SELECT DISTINCT
        Date,
        FIRST_VALUE(Name) OVER w AS FirstName,
        FIRST_VALUE(Certificates) OVER w AS FirstCertificates
    FROM 
    (
        /* Emulate the test dataset. */
        SELECT toDate(data.1) AS Date, data.2 AS Name, data.3 AS Certificates
        FROM (
            SELECT arrayJoin([
                ('2021-02-01', 'Jason ', 3),
                ('2021-02-01', 'Nisha ', 4),
                ('2021-02-01', 'Zaid  ', 5),
                ('2021-03-25', 'Aniket', 4),
                ('2021-03-25', 'Anish ', 2),
                ('2021-03-25', 'Nadia ', 0),
                ('2021-05-06', 'Aadil ', 7),
                ('2021-05-06', 'Ashish', 1),
                ('2021-05-06', 'Rahil ', 9)]) AS data
            )
    )
    WINDOW w AS (PARTITION BY Date ORDER BY Name ASC)
    SETTINGS allow_experimental_window_functions = 1
    
    /*
    ┌───────Date─┬─FirstName─┬─FirstCertificates─┐
    │ 2021-02-01 │ Jason     │                 3 │
    │ 2021-03-25 │ Aniket    │                 4 │
    │ 2021-05-06 │ Aadil     │                 7 │
    └────────────┴───────────┴───────────────────┘
    */
    

    https://altinity.com/blog/clickhouse-window-functions-current-state-of-the-art

    【讨论】:

      【解决方案4】:

      在查看您的输出时,我假设您希望当天的唯一条目是名称列上按字母顺序排列的 ASC 条目。

      在这种情况下,如果此 SQL 服务器可以使用ROW_NUMBER() 函数

      SELECT Date,Name, Certificates
      FROM
      (
      SELECT 
          Date, Name, 
          Certificates=COUNT(Certificates) OVER (PARTITION BY Date,Name) 
          RowNumber = ROW_NUMBER() OVER (PARTITION BY Date
           ORDER BY Name ASC) 
      FROM Students.data
      ) T 
      WHERE RowNumber =1 
      ORDER BY Date ASC
      ;
      

      【讨论】:

      • 所有现代 DBMS 都支持像 row_number() 这样的窗口函数,不仅是 SQL Server(实际上晚了)
      • @trillion 已编辑。感谢您发现
      • @NikunjKakadiya 谢谢。编辑了答案
      猜你喜欢
      • 2021-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多