【问题标题】:Special SQL condition query for finding first two records and the last one?查找前两条记录和最后一条记录的特殊 SQL 条件查询?
【发布时间】:2018-09-28 07:00:12
【问题描述】:

我有一个像这样的数据集:

 student_id     date     project_id
 1              1/1/18   15
 1              1/1/18   17
 1              2/2/18   16 
 1              3/3/18   15 
 1              3/3/18   12 
 2              2/3/18   3
 2              4/3/18   4
 2              5/3/18   6 
 2              5/3/18   4 

我想用他们所做的前两个项目和他们所做的最后一个项目的日期来查找学生 ID,

student_id    project_id1st    date1st     project_id2nd      date2nd projectlast    datelast
1             15               1/1/18      17                 1/1/18  12               3/3/18
2             3                2/3/18      4                  4/3/18  4                5/3/18

首先我想在 pandas 中解决它,但得到了一些不好的结果。然后我尝试用 SQL 解决它。

WITH abc AS (
  SELECT student_id, project_id, date, 
         ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) rn
  FROM table 
)
SELECT student_id, 
       SUM(CASE WHEN rn = 1 THEN abc.project_id END) as firstid,
       SUM(CASE WHEN rn = 2 THEN abc.project_id END) as secondsid,
       MIN(CASE WHEN rn = 1 THEN date END) as first,
       MIN(CASE WHEN rn = 2 THEN date END) as second
FROM abc
GROUP BY 1;

我得到了很好的结果,但不知何故它使用ROW_NUMBER() 弄乱了数据集的顺序。例如,对于学生 1,rownumber 中值为 1 的 project_id 17,project_id 15 将成为第二个开始日期。

【问题讨论】:

  • 我有填写你需要的distinct on(...)postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT
  • 当您有相同的日期时,您的预期订单是多少?如果你想要date,然后是project_id,你必须在order by 子句中说明这一点。
  • @S-Man 在原始数据集中的顺序相同,但是当我使用row_number() 时,它搞砸了
  • 您的问题是您没有指示正确顺序的列。在row_number 窗口框架中,您可以按日期下订单。但是当框架中有很多行时,结果是完全随机的。您必须告诉数据库如何处理同一帧中的行。你想接project_id吗?或者你需要一个时间戳而不是日期?没有“原始订单”这样的指标。这是永远无法保证的。所以你不能得到一个正确的答案。
  • select min(t.test), max(t.test), t.bez, min(x.test) from test t left join ( SELECT test, bez, ROW_NUMBER() OVER (PARTITION BY bez ORDER BY bez) rn FROM test) x on x.bez = t.bez and x.rn = 2 group by t.bez

标签: sql postgresql date


【解决方案1】:

您的问题是您没有指示正确顺序的列。在row_number 窗口框架中,您提供order by date。但是当框架中有很多行时,结果是完全随机的。

您必须告诉数据库如何处理同一帧中的行。你想接project_id吗?没有像“原始订单”这样的指标。

例如

SELECT * FROM table

从不给出专门的顺序(例如插入数据集的顺序)。结果集可以完全随机排序。您定义的任何框架窗口也是如此。

因此,您将需要一种方法(列或算法)来确保您期望的顺序。


如果您选择ORDER BY date, project_id,那么(例如)3/3/18 的框架将使用project_id 12, 15 进行排序,这不是您的“原始订单”。如果您要订购它DESC 您的1/1/18 框架订购错误,因为第一个ID 将是17。所以project_id 不是一个好的订单标准。但是没有其他订购的可能性。那是因为您需要另一列。

要获得您的“原始订单”,一个自动增量 ID 列(键入 serial - 或者如果是 Postgres 10 和更高版本的 GENERATED AS IDENTITY - 会有所帮助)。


当您有特定顺序(例如插入 ID 列)时,这可能是您的查询:

WITH abc AS (
    SELECT insert_id, student_id, project_id, date, 
        ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY insert_id) rn_asc,        -- A
        ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY insert_id DESC) rn_desc   -- B
    FROM projects
)
SELECT 
    student_id,
    MAX(project_id) FILTER (WHERE rn_asc = 1),
    MAX(date) FILTER (WHERE rn_asc = 1),
    MAX(project_id) FILTER (WHERE rn_asc = 2),
    MAX(date) FILTER (WHERE rn_asc = 2),
    MAX(project_id) FILTER (WHERE rn_desc = 1),
    MAX(date) FILTER (WHERE rn_desc = 1)
FROM abc
GROUP BY student_id

A:按 ID 升序对学生帧进行排序,并给出行号 12,这有助于过滤前两行。

B:以相同的降序排列以获得最后一行(在本例中为 row number == 1

demo: db<>fiddle

【讨论】:

    【解决方案2】:

    我会尝试通过简单的连接来完成,例如:

    select min(date), max(date), t.bez, min(x.date) from table t
    left join (
        SELECT date, student_id,
        ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY date) rn
        FROM table
        group by date, student_id) x on x.student_id = t.student_id and x.rn = 2
    group by t.student_id
    

    您可以在 ROW_Number() 中简单地按日期和 student_id 分组,这样您就不会两次获得相同的日期

    【讨论】:

      【解决方案3】:

      使用带有 2 列 project_id 和 date 的 order by

      [Demo]

      WITH abc AS (
        SELECT student_id, project_id, date, 
               ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date,project_id) rn
        FROM table 
      )
      SELECT student_id, 
             SUM(CASE WHEN rn = 1 THEN abc.project_id END) as firstid,
             SUM(CASE WHEN rn = 2 THEN abc.project_id END) as secondsid,
             MIN(CASE WHEN rn = 1 THEN date END) as first,
             MIN(CASE WHEN rn = 2 THEN date END) as second
      FROM abc
      GROUP BY 1;
      

      【讨论】:

      • 这不会像你在这个小提琴中看到的那样工作:dbfiddle.uk/… 对我来说,它显示了不是预期结果的行(学生:1,第一个:12,第二个 15)。
      • 抱歉,已修复
      • @S-Man,我已经按选项更改了顺序,它起作用了
      • dbfiddle.uk/… 如您所见,您的 row_number() 没有按预期工作。因为你是按project_id排序的,所以最后的顺序是错误的。 TO 预计 project_id 12 是最后一个,但因为您的订单是最后一个 15。
      【解决方案4】:

      请尝试转换为整数。

      与 abc AS (SELECT student_id, project_id, date,

      CAST(ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) AS INT) rn FROM table )

      【讨论】:

      • 这毫无意义。 row_number 已经是一个 bigint。将其更改为整数不会有任何区别
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多