【问题标题】:在最后一条记录上选择带有分组依据的整行
【发布时间】:2022-01-19 13:40:13
【问题描述】:

我试图只从 group by 中选择最后一个,但每次都失败。

使用此查询:

SELECT
    s.NABAVNACENA, s.ROBAID, MAX(d.DATUM) AS DATUM
FROM 
    DOKUMENT d
LEFT OUTER JOIN 
    STAVKA s ON s.VRDOK = d.VRDOK AND s.BRDOK = d.BRDOK
WHERE
    d.VRDOK IN (0, 11, 18, 16, 22, 1, 2, 26, 29, 30)
    AND d.DATUM >= '01.01.2021'
    AND d.DATUM <= '31.12.2021'
    AND s.ROBAID IN (39, 34)
GROUP BY 
    s.ROBAID, s.NABAVNACENA

我得到了这些结果

NABAVNACENA   ROBAID   DATUM
---------------------------------
149           39       01.01.2021
130           39       03.01.2021
137           39       08.01.2021
847           34       02.01.2021
820           34       03.01.2021

但是,所需的结果只有两行,由 ROBAID 分组,最大日期。

在这种情况下:

137     39    08.01.2021
820     34    03.01.2021

当我从查询中排除 s.NABAVNACENA 时,它确实只返回这两行,但当我包含它时,它会显示所有内容。

我从 Stack Overflow 尝试了很多东西,但我数不清了,现在我不知道该怎么办了。

这是表结构

/* Table: STAVKA, Owner: SYSDBA */

CREATE TABLE "STAVKA" 
(
    "STAVKAID"   INTEGER NOT NULL,
    "VRDOK"      SMALLINT NOT NULL,
    "BRDOK"      INTEGER NOT NULL,
    "MAGACINID"  SMALLINT NOT NULL,
    "ROBAID"       INTEGER NOT NULL,
    "VRSTA"        SMALLINT,
    "NAZIV"        VARCHAR(200),
    "NABCENSAPOR"  NUMERIC(15,4),
    "FAKTURNACENA" NUMERIC(15,4),
    "NABCENABT"    DOUBLE PRECISION,
    "TROSKOVI"     NUMERIC(15,4),
    "NABAVNACENA"  NUMERIC(15,4) NOT NULL,
    "PRODCENABP"   NUMERIC(15,4) NOT NULL,
    "KOREKCIJA"    DOUBLE PRECISION,
    "PRODAJNACENA" NUMERIC(15,2) NOT NULL,
    "DEVIZNACENA"  NUMERIC(15,4) NOT NULL,
    "DEVPRODCENA"  NUMERIC(15,4),
    "KOLICINA"     NUMERIC(15,3) NOT NULL,
    "NIVKOL"       NUMERIC(15,3) NOT NULL,
    "TARIFAID"     VARCHAR(3),
    "IMAPOREZ"     SMALLINT,
    "POREZ"        NUMERIC(15,2) NOT NULL,
    "RABAT"        NUMERIC(15,2) NOT NULL,
    "MARZA"        NUMERIC(15,2) NOT NULL,
    "TAKSA"        NUMERIC(15,4),
    "AKCIZA"       NUMERIC(15,2),
    "PROSNAB"      NUMERIC(15,4) NOT NULL,
    "PRECENA"      NUMERIC(15,4) NOT NULL,
    "PRENAB"       NUMERIC(15,4) NOT NULL,
    "PROSPROD"     NUMERIC(15,4) NOT NULL,
    "MTID"         VARCHAR(10),
    "PT"           CHAR(1) NOT NULL,
    "ZVEZDICA"     VARCHAR(6),
    "TREN_STANJE"  NUMERIC(15,3),
    "POREZ_ULAZ"   NUMERIC(15,2) NOT NULL,
    "SDATUM"       DATE,
    "DEVNABCENA"   NUMERIC(15,4),
    "POREZ_IZ"     NUMERIC(15,2) NOT NULL,
    "X4"           NUMERIC(15,3),
    "Y4"           NUMERIC(15,3),
    "Z4"           NUMERIC(15,3),
    "CENAPOAJM"    NUMERIC(15,2),
    "KGID"         INTEGER,
    "SAKCIZA"      NUMERIC(15,4) NOT NULL,

    CONSTRAINT "STAVKAPRIMARYKEY" PRIMARY KEY ("STAVKAID")
);

我想导出数据,但是有 500k 行,我不知道它会在这里。

所以基本上STAVKADOCUMENT_ITEM 在一些DOCUMENT 中 该项目包含ProductID (ROBAID)Price (NABAVNACENA) 以及该文档中该产品的其他数据。

所以现在我想遍历所有STAVKA (DOCUMENT_ITEM) 并为每个ROBAID 选择最后一个Price (NABAVNACENA)

对于单身,我设法这样做:

SELECT
    t1.STAVKAID, s.ROBAID, s.NABAVNACENA
FROM
    (SELECT FIRST 1 s.STAVKAID, d.DATUM
     FROM DOKUMENT d
     LEFT OUTER JOIN STAVKA s ON s.VRDOK = d.VRDOK AND s.BRDOK = d.BRDOK
     WHERE d.VRDOK IN (0, 11, 18, 16, 22, 1, 2, 26, 29, 30)
       AND d.DATUM >= '01.01.2021'
       AND d.DATUM <= '31.12.2021'
       AND s.ROBAID = 39
    ORDER BY DATUM DESC) AS t1 
LEFT OUTER JOIN 
    STAVKA s ON t1.STAVKAID = s.STAVKAID

它返回多行,但我按DATUM DESC 对它们进行排序并选择第一行,这样我就为ROBAID 选择了最后一个STAVKA

由于我有 4k ROBAID,我不想从我的代码中运行此查询 4k 次,而是将整个表返回给它

【问题讨论】:

  • 您使用的是哪个 Firebird 版本?在任何情况下,当以这种方式使用GROUP BY 时,这个结果完全是预期的,因为包含NABAVNACENA 将使它确定NABAVNACENA 的每个不同值的最大日期。
  • 2.1 版。不能再高了,不在我的能力范围内。
  • NABAVNACENA、ROBAID 和 DATUM 是否会有(最多)一行,或者 NABAVNACENA、ROBAID 是否可以有多行具有相同 DATUM?
  • @MarkRotteveel 可能有 0.001% 的可能性,但我不想把它包括在这个问题中,因为我脑子里有方向我会尝试用 limit 1max/min 来解决它像这样
  • 你为什么在S上做LEFT OUTER JOIN?看到s.NABAVNACENA, s.ROBAID 是第一个、最有意义的专栏,甚至是群组制作,这看起来很奇怪?您打算如何处理其中一个或两个中有 NULL 的行?

标签: sql greatest-n-per-group firebird firebird2.1


【解决方案1】:

SQL 中真正有用的事情之一是您几乎总是可以用查询替换 from 子句中的表名:

SELECT
    s.NABAVNACENA, x.ROBAID, x.DATUM
    from DOKUMENT d
    LEFT OUTER JOIN STAVKA s ON s.VRDOK = d.VRDOK AND s.BRDOK = d.BRDOK
    inner join
    (
      select s.ROBAID, max(d.DATUM) DATUM
      from DOKUMENT d
      LEFT OUTER JOIN STAVKA s ON s.VRDOK = d.VRDOK AND s.BRDOK = d.BRDOK
      WHERE
      d.VRDOK IN (0, 11, 18, 16, 22, 1, 2, 26, 29, 30)
      AND d.DATUM >= '01.01.2021'
      AND d.DATUM <= '31.12.2021'
      AND s.ROBAID IN (39, 34)
      GROUP BY s.ROBAID
    ) x on s.Robaid = x.robaid and d.datum = x.datum

额外的内连接是引入需要但没有 NABAVNACENA 的两行的查询,并使用它将原始查询限制为这两行。

【讨论】:

  • 第一个错误弹出,因为在第四行有额外的from。第二个错误是Unknown column x.datum 来自最后一行,在and 之后
  • 好的。几个错别字:去掉额外的行,在子查询中我将 max(d.datum) 错误地命名为 DATUS 而不是 DATUM。我现在就修。
  • 这里是回复:image。对于给定的ROBAID,不仅最后NABAVNACENA
  • 好的,所以 STAVKA 中有多行与所需的 ROBAID 和 DATUM 匹配。你想要哪一个?
  • 一个有MAX(DATUM)的,就像在例子中一样
【解决方案2】:

您可以将表连接到自身上吗?我最好使用合成 ID(总是递增主键,例如某些数据库中的 AutoInc 字段,FB2 中的 Generator+Trigger),但是好吧,让我们尝试使用日期。只是...请注意不要在一个组中包含两行或多行具有完全相同的日期。

帮助将在 FB 2.1 中引入 CTE:https://firebirdsql.org/refdocs/langrefupd21-select.html#langrefupd21-select-cte

WITH DOKSTAV AS (
  SELECT 
    s.NABAVNACENA, s.ROBAID, d.DATUM
  FROM 
    DOKUMENT d, STAVKA s 
  WHERE s.VRDOK = d.VRDOK 
    AND s.BRDOK = d.BRDOK
)

SELECT
  s1.NABAVNACENA, s1.ROBAID, s1.DATUM
FROM 
  DOKSTAV s1 
LEFT OUTER JOIN 
  DOKSTAV s2
       ON (s1.NABAVNACENA = s2.NABAVNACENA)
      AND (s1.ROBAID = s2.ROBAID)
      AND (s1.DATUM < s2.DATUM)
WHERE s2.DATUM IS NULL 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-05
    • 2016-02-14
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 2014-02-13
    相关资源
    最近更新 更多