How to exclude rows from sum but still show them? - sql

I have a table itemsInShippment with the following data:
itemid shippmentid qty
10 1 100
20 1 200
10 2 300
10 3 1000
and table shippments
shippmentid date shippmentstatus supplierid
1 2015-01-12 OK 5000
2 2015-01-17 OK 5000
3 2015-01-17 Cancelled 5000
I need to write a query that shows this details about specific shippment say shipmentid 1. My given parameters are supplierid and date. together they related to one shipment (unique).
For supplierid=5000 and date=2015-01-12 I want to get:
itemid qty qtyInOtherShipments
10 100 300 //1000 is canceled.
20 200 0
My query works fine without considering the cancelled:
SELECT cte.*
FROM
(SELECT
a.itemid, b.date, a.qty,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.shipmentid) ,0) ) AS qtyInOtherShipments,
FROM
itemsInShippment a
LEFT JOIN
shippments b using (shippmentid)
WHERE
b.supplierid = 5000) AS cte
WHERE
cte.date = '2015-01-12'
the cte must be this way as in qtyInOtherShipments I Sum the total qty and then remove my own qty. In order to sum the total qty I can't do WHERE d.date=... inside I must do that outside.
This query gives:
itemid qty qtyInOtherShipments
10 100 1300
20 200 0
I'm having trouble taking under consideration the cancelled shipments.
if I change the Where to :
where b.supplierid = 5000 and b.shippmentstatus not like 'cancelled'
it works... I will see:
itemid qty qtyInOtherShipments
10 100 300
20 200 0
but if I run the query on cancelled shipments (supplierid=5000 and date=2015-01-17) I will get:
itemid qty qtyInOtherShipments
nothing
what I should have get is:
itemid qty qtyInOtherShipments
10 1000 300
so my problem is that I don't want to sum itemid that is related to cancelled but I still want to see this rows.
How do I get the correct result?

You want to exclude canceled items only from sums. So, do not filter them with where, just filter them on sums:
SUM(case when b.shippmentstatus <> 'cancelled' then a.qty end) OVER (PARTITION BY ...
Sum does not take in consideration null, that's why the above works. (When status is canceled the case expression will return null.)

A more efficient variant of Florian's answer exists for PostgreSQL 9.4, the filter clause for an aggregate.
SUM (a.qty) FILTER (WHERE b.shippmentstatus <> 'cancelled') OVER (PARTITION BY ...
See FILTER in the docs for aggregates. It's basically a mini-WHERE clause that applies only for that aggregate.
Thanks to #a_horse_with_no_name for pointing it out earlier.

Try Below query
create table #itemsInShippment (itemid int, shippmentid int, qty int)
insert into #itemsInShippment (itemid, shippmentid, qty)
SELECT 10 as itemid, 1 as shippmentid, 100 as qty UNION
SELECT 20 , 1, 200 UNION
SELECT 10 , 2, 300 UNION
SELECT 10 , 3, 1000
CREATE TABLE #shippments (shippmentid int , dt date, shippmentstatus varchar(50), supplierid int)
insert into #shippments (shippmentid, dt, shippmentstatus,supplierid)
SELECT 1 as shippmentid, '2015-01-12' as dt, 'OK' as shippmentstatus , 5000 as supplierid UNION ALL
SELECT 2, '2015-01-17', 'OK' , 5000 UNION ALL
SELECt 3, '2015-01-17' , 'Cancelled' , 5000
SELECT cte.*
FROM (
select a.itemid,b.dt,a.qty,
(coalesce( SUM(case when shippmentstatus <> 'Cancelled' then a.qty else 0 end) OVER (PARTITION BY a.itemid) ,0) -
coalesce( SUM(case when shippmentstatus <> 'Cancelled' then a.qty else 0 end) OVER (PARTITION BY a.itemid,a.shippmentid) ,0) )
AS qtyInOtherShipments
from #itemsInShippment a
left join #shippments b on a.shippmentid = b.shippmentid
where b.supplierid = 5000 --and shippmentstatus = 'Cancelled'
) as cte
where cte.dt='2015-01-12'

Related

How to merge two query results joining same date

let's say there's a table have data like below
id
status
date
1
4
2022-05
2
3
2022-06
I want find count of id of each month by their status. Something like this below
date
count(status1) = 4
count(status2) =3
2022-05
1
null
2022-06
null
1
I tried doing
-- select distinct (not working)
select date, status1, status2 from
(select date, count(id) as "status1" from myTable
where status = 4 group by date) as myTable1
join
(select date, count(id) as "status2" from myTable
where status = 3 group by date) as myTable2
on myTable1.date = myTable2.date;
-- group by (not working)
but it does duplicate the data needed.
and I am using SQL Server.
select d.date,
sum
(
case
when d.status=4 then 1
else 0
end
)count_status_4,
sum
(
case
when d.status=5 then 1
else 0
end
)count_status_5
from your_table as d
group by d.date

Filter results within groups sql

In the table below, I want to know how many customers ordered lunch without a coffee. The result would be 1, for sale ID 300, because two lunches were ordered but only one coffee.
It’s been 8 years since I last used SQL! How do I say “group the records by sale ID and for each group, drop groups where there is no lunch or COUNT(coffee) < COUNT(lunch)"?
SALE ID
Product
100
coffee
100
lunch
200
coffee
300
lunch
300
lunch
300
coffee
here is one way:
select count(*) from (
select saleID
from tablename
group by saleID
having sum(case when product ='coffee' then 1 else 0 end) = 0
and sum(case when product ='lunch' then 1 else 0 end) = 1
) t
You can do it with aggregation and the conditions in the HAVING clause.
This query:
SELECT sale_id
FROM tablename
GROUP BY sale_id
HAVING SUM(product = 'lunch') > SUM(product = 'coffee');
returns all the sale_ids that you want.
This query:
SELECT DISTINCT COUNT(*) OVER () counter
FROM tablename
GROUP BY sale_id
HAVING SUM(product = 'lunch') > SUM(product = 'coffee');
returns the number of sale_ids that you want.
See the demo.
select count(*) from (
--in this subquery calculate counts and ignore items that haven't any lunch
select
saleID, sum(case when product ='coffee' then 1 else 0 end) as coffee,
sum(case when product ='lunch' then 1 else 0 end) lunch
from tablename
group by saleID
having sum(case when product ='lunch' then 1 else 0 end) >= 1 --Here we are ignoring all items haven't any lunch
) t
where lunch > coffee -- we check second condition be ok

Count same column twice based on condition

i have the following query to count the Amount field as Countused if Account_Fkey is not null
select Amount as amount2, Count(Amount) as CountUsed from tblGiftCards
where Account_Fkey is not null
group by Amount
result:
amount2 CountUsed
25 3
50 10
100 5
i want to calculate the amount of Amount as Amount not used in the same query where account fkey is null. So, the result will be:
amount2 CountUsed CountUnused
25 3 1
50 10 0
100 5 2
Thanks
You could use a conditional aggregation
select Amount as amount2
, sum( case when Account_Fkey is not null
and Amount is not null then 1 else 0 end) CountUsed
, sum( case when Account_Fkey is null
and Amount is not null then 1 else 0 end) CountNotUsed
from tblGiftCards
group by Amount
Try
select tab.amount2 , CountUsed , CountUnUsed from
(select Amount as amount2, Count(Amount) as CountUsed from tblGiftCards
where Account_Fkey is not null
group by Amount
) tab,
(select Amount as amount2, Count(Amount) as CountUnUsed from tblGiftCards
where Account_Fkey is null
group by Amount
)tab2
where tab.amount2 = tab2.amount2
I would simplify the logic and express this as:
select Amount as amount2, count(Account_Fkey) as CountUsed,
(count(*) - count(Account_Fkey)) as CountNotUsed
from tblGiftCards
where Amount is not null
group by Amount

Merge or group rows corresponding to particular column postgresql

I want to group by the resultset further corresponding to the price_type column,
if the data for a product with both price_type variant and parent_product is present, then it must show only the variant one
For example, this data
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Shorts 1 10 21 100 parent_product
Night Suit 1 10 22 200 variant
Night Suit 1 10 22 100 parent_product
Denim 1 10 23 400 parent_product
should come like
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Night Suit 1 10 22 200 variant
Denim 1 10 23 400 parent_product
It seems you want row_number() with conditional ordering:
select *
from (select *, row_number() over (partition by ProductName
order by (case when Price_type = 'variant'
then 0 else 1
end)
) as seq
from table
) t
where seq = 1;
Below is the simple query to get desired result.
select
distinct on(product_name),
t.*
from tab t
order by price_type='variant' desc
You can use a window function:
SELECT * FROM
(
SELECT * ,
RANK() OVER (PARTITION BY product_name ORDER BY priority ) AS rank
FROM (
SELECT *,
CASE
WHEN price_type='variant' THEN 1
ELSE 2
END AS priority
FROM yourtable
) AS X
) AS Y
WHERE rank=1

Group By by hiding a column - TSQL

I have a table structure
Table1
ID Hours Qty ProductID
1 2 1 100
1 3 5 200
2 6 6 100
2 2 2 200
If productid is (1,2,3) then i need sum ( Qty * Hours),If productid in (200,300,400,500) then i need sum(qty).
I have written a code like this
select ID,case when productid in (1,2,3) then
SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty ) end result1
from Prod group by id ,productid
but i don't want to group by productid,i would like to pass it in "IN clause".How to achieve it.
Move the SUM() outside of the CASE WHEN statement.
SELECT
ID,
SUM(case when productid in (1,2,3) then qty * hrs
when productid in (100,200,300) then qty
end) result1
FROM
Prod
GROUP BY
ID
Assuming you want all columns plus the result of your query, you can do this:
select p.*, aux.result
from Prod p
inner join (select ID,case when productid in (1,2,3) then SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty )
end as result
from Prod group by id ,productid) aux on aux.id = p.id