SQL query get sum of sums - sql

Please help me with the problem:
I have query:
SELECT drugs_shipment.drug_id,
drugs_drug.name AS drug_name,
drugs_drugunit.name AS drug_unit,
drugs_shipment.initial_amount - SUM(IFNULL(drugs_movement.amount, "0")) OVER (PARTITION BY drugs_shipment.id) AS total_amount
FROM drugs_shipment
JOIN drugs_drug ON drugs_shipment.drug_id = drugs_drug.id
JOIN drugs_drugunit ON drugs_drug.unit_id = drugs_drugunit.id
LEFT JOIN drugs_movement ON (drugs_movement.shipment_id = drugs_shipment.id AND drugs_movement.DATE < "2025-12-11" )
WHERE drugs_shipment.date_of_comming < "2025-12-11"
AND (drugs_shipment.date_of_run_out IS NULL OR drugs_shipment.date_of_run_out > "2025-12-11")
Next, i need sum of total_amount in fields with the same drugs_shipment.drug_id
What is whrong? How to solve this problem?
But this doesn't work:
SELECT drugs_shipment.drug_id,
drugs_drug.name AS drug_name,
drugs_drugunit.name AS drug_unit,
SUM (drugs_shipment.initial_amount - SUM(IFNULL(drugs_movement.amount, "0")) OVER (PARTITION BY drugs_shipment.id)) AS total_amount
FROM drugs_shipment
JOIN drugs_drug ON drugs_shipment.drug_id = drugs_drug.id
JOIN drugs_drugunit ON drugs_drug.unit_id = drugs_drugunit.id
LEFT JOIN drugs_movement ON (drugs_movement.shipment_id = drugs_shipment.id AND drugs_movement.DATE < "2025-12-11" )
WHERE drugs_shipment.date_of_comming < "2025-12-11"
AND (drugs_shipment.date_of_run_out IS NULL OR drugs_shipment.date_of_run_out > "2025-12-11")
GROUP BY drugs_shipment.drug_id

You are not allowed using window functions (the OVER clause) and grouping (the GROUP BY clause) at the same time. You need to use another window function or nested query to do this.
SELECT drug_id
,drug_name
,drug_unit
,SUM(total_amount) OVER (PARTITION BY drug_id)
FROM
(
SELECT drugs_shipment.drug_id,
drugs_drug.NAME AS drug_name,
drugs_drugunit.NAME AS drug_unit,
drugs_shipment.initial_amount - Sum(Ifnull(drugs_movement.amount, "0")) OVER (partition BY drugs_shipment.id) AS
total_amount
FROM drugs_shipment
JOIN drugs_drug
ON drugs_shipment.drug_id = drugs_drug.id
JOIN drugs_drugunit
ON drugs_drug.unit_id = drugs_drugunit.id
LEFT JOIN drugs_movement
ON ( drugs_movement.shipment_id = drugs_shipment.id
AND drugs_movement.date < "2025-12-11" )
WHERE drugs_shipment.date_of_comming < "2025-12-11"
AND ( drugs_shipment.date_of_run_out IS NULL
OR drugs_shipment.date_of_run_out > "2025-12-11" )
) DS

Related

SQL Server aggregate function without group by

I want to include tcon.Inductive_Injection_Hours, tcon.Capacitive_Injection_Hours without applying group by. How can I do that?
SELECT
bp.Serial_Number,
tcon.Serial_Number AS ConverterSerialNumber,
MAX(tcon.Time_Stamp) AS DateStamp,
tcon.Inductive_Injection_Hours,
tcon.Capacitive_Injection_Hours
FROM
dbo.Bypass AS bp
INNER JOIN
dbo.Converter AS c ON bp.Bypass_ID = c.Bypass_ID
INNER JOIN
dbo.Converter_Tel_Data AS tcon ON c.Converter_ID = tcon.Converter_ID
WHERE
(bp.Site_ID = 7)
GROUP BY
bp.Serial_Number, tcon.Serial_Number,
tcon.Inductive_Injection_Hours, tcon.Capacitive_Injection_Hours
ORDER BY
ConverterSerialNumber
I have figured it out.
select [data].Serial_Number,Time_Stamp,Inductive_Injection_Hours,Capacitive_Injection_Hours,b.Serial_Number from Converter_Tel_Data as [data]
inner join dbo.Converter AS c On [data].Converter_ID = c.Converter_ID
inner join dbo.Bypass as b on c.Bypass_ID = b.Bypass_ID
WHERE
(Time_Stamp = (SELECT MAX(Time_Stamp) FROM Converter_Tel_Data WHERE Converter_ID = [data].Converter_ID)) And ([data].Site_ID=7)
ORDER BY [data].Serial_Number
You can use row_number - either in a CTE/derived table or using a trick with TOP 1.
Select Top 1 With Ties
bp.Serial_Number
, tcon.Serial_Number AS ConverterSerialNumber
, tcon.Time_Stamp AS DateStamp
, tcon.Inductive_Injection_Hours
, tcon.Capacitive_Injection_Hours
From dbo.Bypass AS bp
Inner Join dbo.Converter AS c On bp.Bypass_ID = c.Bypass_ID
Inner Join dbo.Converter_Tel_Data AS tcon ON c.Converter_ID = tcon.Converter_ID
Where bp.Site_ID = 7
Order By
row_number() over(Partition By bp.Serial_Number Order By tcon.Time_Stamp desc)
This should return the latest row from the tconn table for each bp.Serial_Number.

SQL - Show all values from the first tabel

I want to show all values of a column 'nation.nationen_bez' in the tabel 'nation' using this query:
SELECT DISTINCT nation.nationen_bez,
NVL(SUM (debitor.flimit_deb) OVER (PARTITION BY nation.nationen_bez ORDER BY nation.nationen_bez),0) AS korisceno,
NVL(nation_ext.country_limit,0) - NVL(SUM (debitor.flimit_deb) OVER (PARTITION BY nation.nationen_bez ORDER BY nation.nationen_bez),0) AS se_na_voljo,
NVL(nation_ext.country_limit,0) AS odobren_limit
FROM debitor,
nation,
nation_ext,
firmenstamm,
debitorenstamm
WHERE ( nation.nationen_kode = debitorenstamm.nationen_kode (+)) and
( nation.nationen_id = nation_ext.nationen_id (+)) and
( debitor.debitoren_id = debitorenstamm.debitoren_id ) and
( debitorenstamm.waehrungs_id = firmenstamm.waehrungs_id ) and
( ( debitor.risiko = 1 ) AND
( debitor.factoringart = 'EF' ))
With this query I get only those 'nation.nationen_bez' where exist debitors with debitor.risiko = 1 and
debitor.factoringart = 'EF'. This conditioin is needed for 'NVL(SUM (debitor.flimit_deb) OVER (PARTITION BY nation.nationen_bez ORDER BY nation.nationen_bez),0) AS korisceno', because I want sum only 'flimit_deb' for those debtors that have debitor.risiko = 1 and debitor.factoringart = 'EF' - how can I change this state?
tnx in advance
I have used left join, started with nation
SELECT DISTINCT nation.nationen_bez,
SUM (debitor.flimit_deb) OVER (PARTITION BY nation.nationen_bez ORDER BY nation.nationen_bez) AS korisceno,
nation_ext.country_limit - SUM (debitor.flimit_deb) OVER (PARTITION BY nation.nationen_bez ORDER BY nation.nationen_bez) AS se_na_voljo,
nation_ext.country_limit AS odobren_limit
FROM nation
left join debitorenstamm on nation.nationen_kode = debitorenstamm.nationen_kode
left join debitor on debitorenstamm.debitoren_id = debitor.debitoren_id and debitor.risiko = 1 AND debitor.factoringart = 'EF'
left join nation_ext on nation.nationen_id = nation_ext.nationen_id
left join firmenstamm on debitorenstamm.waehrungs_id = firmenstamm.waehrungs_id

where condition to the on clause

I need to modify this sql using a case condition, I need qty to be populated as '1' if serial_number table have the inventory tag available (serial_number.tag_id = inventory.tag_id) else the qty needs to populate from the query results
What would be the best way to do this?
SELECT
* from ( SELECT
inventory.tag_id PalletNumber,
inventory.sku_id sku,
inventory.batch_id batch,
**inventory.qty_on_hand qty,**
inventory.condition_id status,
inventory.user_def_type_1 reasoncode,
serial_number.serial_number serialnumber,
TO_CHAR(inventory.expiry_dstamp, 'YYYY-MM-DD') Expiry_Date,
pre_advice_header.pre_advice_id asn,
pre_advice_header.status asnstatus,
inventory.supplier_id vendor,
TO_CHAR(inventory.receipt_dstamp, 'YYYY-MM-DD') ArrivalDate,
pre_advice_line.host_pre_advice_id PONumber
FROM
inventory
JOIN pre_advice_header ON pre_advice_header.pre_advice_id = inventory.receipt_id
AND pre_advice_header.site_id = inventory.site_id
AND pre_advice_header.client_id = inventory.client_id
AND pre_advice_header.status in ('Complete')
AND pre_advice_header.status is not NULL
LEFT OUTER JOIN pre_advice_line ON pre_advice_line.pre_advice_id = inventory.receipt_id
AND pre_advice_line.sku_id = inventory.sku_id
LEFT JOIN serial_number ON **serial_number.tag_id = inventory.tag_id**
AND serial_number.site_id = inventory.site_id
AND serial_number.client_id = inventory.client_id
AND serial_number.receipt_id = inventory.receipt_id
AND serial_number.sku_id = inventory.sku_id
WHERE
inventory.site_id = 'UK-CBY-04'AND inventory.client_id = 'MLC796'
AND (inventory.condition_id != 'SC1' or inventory.condition_id is null)
AND (inventory.zone_1 <> '80SHP01' or inventory.zone_1 is null)
GROUP BY
inventory.tag_id,
inventory.sku_id,
inventory.batch_id,
inventory.qty_on_hand,
inventory.condition_id,
inventory.user_def_type_1,
serial_number.serial_number,
inventory.expiry_dstamp,
pre_advice_header.pre_advice_id,
pre_advice_header.status,
inventory.supplier_id,
inventory.receipt_dstamp,
pre_advice_line.host_pre_advice_id
ORDER BY
inventory.sku_id DESC,
inventory.tag_id ASC
)
The solution will be as follows using CASE:
CASE WHEN serial_number.serial_number IS NOT NULL THEN 1 ELSE inventory.qty_on_hand END qty

Should a subquery on a join use tables from an outer query in the where clause?

I need to add a subquery to a join, because one payment can have more than one allotment, so I only need to account for the first match (where rownum = 1).
However, I'm not sure if adding pmt from the outer query to the subquery on the allotment join is best.
Should I be doing this differently in the event of performance hits, etc.. ?
SELECT
pmt.payment_uid,
alt.allotment_uid,
FROM
payment pmt
/* HERE: is the reference to pmt.pay_key and pmt.client_id
incorrect in the below subquery? */
INNER JOIN allotment alc ON alt.allotment_uid = (
SELECT
allotment_uid
FROM
allotment
WHERE
pay_key = pmt.pay_key
AND
pay_code = 'xyz'
AND
deleted = 'N'
AND
client_id = pmt.client_id
AND
ROWNUM = 1
)
WHERE
AND
pmt.deleted = 'N'
AND
pmt.date_paid >= TO_DATE('2017-07-01')
AND
pmt.date_paid < TO_DATE('2017-10-01') + 1;
It's difficult to identify the performance issue in your query without seeing an explain plan output. You query does seem to do an additional SELECT on the allotment for every record from the main query.
Here is a version which doesn't use correlated sub query. Obviously I haven't been able to test it. It does a simple join in and then filters all records except one of the allotments. Hope this helps.
WITH v_payment
AS
(
SELECT
pmt.payment_uid,
alt.allotment_uid,
ROW_NUMBER () OVER(PARTITION BY allotment_id) r_num
FROM
payment pmt JOIN allotment alt
ON (pmt.pay_key = alt.pay_key AND
pmt.client_id = alt.client_id)
WHERE pmt.deleted = 'N' AND
pmt.date_paid >= TO_DATE('2017-07-01') AND
pmt.date_paid < TO_DATE('2017-10-01') + 1 AND
alt.pay_code = 'xyz' AND
alt.deleted = 'N'
)
SELECT payment_uid,
allotment_uid
FROM v_payment
WHERE r_num = 1;
Let's know how this performs!
You can phrase the query that way. I would be more likely to do:
SELECT . . .
FROM payment p INNER JOIN
(SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY pay_key, client_id
ORDER BY allotment_uid
) as seqnum
FROM allotment a
WHERE pay_code = 'xyz' AND deleted = 'N'
) a
ON a.pay_key = p.pay_key AND a.client_id = p.client_id AND
seqnum = 1
WHERE p.deleted = 'N' AND
p.date_paid >= DATE '2017-07-01' AND
p.date_paid < (DATE '2017-10-01') + 1;

PostgreSQL Aggregate function select 2 corresponding fields

I have the following sub query:
LEFT OUTER JOIN
(
SELECT
MAX(testresult."testdate") AS beforeresult,
testschedule."test_id",
testschedule."asset_id",
testschedule."testdate" AS testdate,
testschedule."testresult_id" as scheduleresult_id
FROM
"public"."testresult" testresult
INNER JOIN "public"."testschedule" testschedule
ON testschedule."asset_id" = testresult."asset_id"
AND testschedule."test_id" = testresult."test_id"
WHERE
"testresult"."client_id" = 25368272
AND testresult."testdate" < testschedule."mintolerancedate"
AND testschedule."testdate" > '2016-10-01'
AND testschedule."testdate" < '2016-10-20'
GROUP BY
testschedule."asset_id",
testschedule."test_id",
testschedule."testdate",
testschedule."testresult_id"
ORDER BY MAX(testresult."testdate")
) lasttr
ON "testschedule"."asset_id" = lasttr."asset_id"
AND "testschedule"."test_id" = lasttr."test_id"
AND testschedule."testdate" = lasttr.testdate
This gives me the correct testdate. However, I also need the testschedule."testresult_id" that corresponds with the date. Is there a way to select this from the above query?
You can fiddle with window functions or in the most recent versions, do a left lateral join:
LEFT JOIN LATERAL
(SELECT ts."test_id", ts."asset_id", ts."testdate" AS testdate, ts."testresult_id" as scheduleresult_id,
tr.*
FROM "public"."testresult" tr INNER JOIN
"public"."testschedule" ts
ON ts."asset_id" = tr."asset_id" and ts."test_id" = tr."test_id"
WHERE tr."client_id" = 25368272 AND
tr."testdate" < ts."mintolerancedate" AND
ts."testdate" > '2016-10-01' and ts."testdate" < '2016-10-20' AND
"testschedule"."asset_id" = ts."asset_id" AND
"testschedule"."test_id" = ts."test_id"
ORDER BY tr."testdate" DESC
FETCH FIRST 1 ROW ONLY
) lasttr
A lateral join is like a correlated subquery in the FROM clause. You can return as many columns as you like. There is no ON clause because the join conditions are in the subquery's WHERE clause.