Home > Back-end >  Select entire row with group by on last record
Select entire row with group by on last record

Time:12-17

I am trying to select only last from group by but failing each time.

With this query:

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

I am getting these results

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

However, the desired result is only two rows grouped by ROBAID with max date.

In this case:

137     39    08.01.2021
820     34    03.01.2021

When I exclude s.NABAVNACENA from query it does return only these two rows, but when I include it it shows everything.

I tried a lot of things from Stack Overflow that I lost count and now I do not know what to do.

EDIT:

Here is table structure

/* 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")
);

I would like to export data but there are 500k rows and do not know how it would be here.

So basically STAVKA is DOCUMENT_ITEM inside some DOCUMENT That item holds ProductID (ROBAID), Price (NABAVNACENA) and other data that are for that product in that document.

So now I want to go through all STAVKA (DOCUMENT_ITEM) and select last Price (NABAVNACENA) for each ROBAID.

For single one I managed to do it like this:

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

It returns multiple rows, but I order them by DATUM DESC and select first one, that way I selected last STAVKA for that ROBAID

Since I have 4k ROBAID I do not want to run this query 4k times from my code but to return whole table to it

CodePudding user response:

One of the really useful things in SQL is that you can nearly always replace a tablename in a from clause with a query:

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

The extra inner join is the query which brings in the two rows wanted but doesn't have NABAVNACENA, and uses it to restrict the original query to those two rows.

CodePudding user response:

Can you just join the table onto itself? I would better use synthetic ID (always incrementing primary key, like AutoInc field in some databases, Generator Trigger in FB2), but okay, let's try with dates. Just... Take care that you would not have TWO or more rows with exactly the same date in one group.

Helping would be CTE introduced in FB 2.1: 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 
  • Related