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
Related
I have a sales table:
date, user_id, product
there are 26 products(a-z), and those users who have purchased both 'a' and 'b' product are classified as acquired customers.
What I want is the daily level count of acquired customers as a SQL query
Say for eg, A user 'X' bought product 'a' on 1st apr, and bought product 'b' on 20th apr then he will be deemed as acquired on 20th apr.
Need a SQL query for this
Sample data:
date user_id Product sale
01-04-2019 123 a 200
01-04-2019 234 b 300
01-04-2019 345 a 200
02-04-2019 123 b 300
03-04-2019 234 b 300
04-04-2019 555 g 400
05-04-2019 666 a 200
05-04-2019 666 b 300
Desired Output from sql query:
date ac-quired_users
01-04-2019 0
02-04-2019 1
03-04-2019 0
04-04-2019 0
05-04-2019 1
obviously there will be a lot more data
You can use window functions for this. First, get the "start" date for each user:
select userid, min(date)
from (select t.*,
sum(case when product = 'a' then 1 else 0 end) over (partition by userid order by date) as cnt_a,
sum(case when product = 'b' then 1 else 0 end) over (partition by userid order by date) as cnt_b
from t
) t
from t
group by userid;
Then aggregate this:
select date, count(*)
from (select userid, min(date)
from (select t.*,
sum(case when product = 'a' then 1 else 0 end) over (partition by userid order by date) as cnt_a,
sum(case when product = 'b' then 1 else 0 end) over (partition by userid order by date) as cnt_b
from t
) t
from t
group by userid
) u
group by date
order by date;
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'
`I have 2 tables:incomes and sales and I want to show in query:
a) sum of incomes
b) sum of sales
c) quantity of products now - difference between a-b
table 'incomes' has fields:Incomeid,ProductId,quant etc.
table 'sales' has fields:SalesId, IncomeId, ProductId, quant etc.
My SQL is:
select i.productId, sum(i.quant) as iq, sum(s.quant) as sq
from income i,sales s
where (i.incomeId=s.incomeId)
group by i.productId
or
select i.productId, sum(i.quant) as iq, sum(s.quant) as sq
from income
left outer join s on (i.IncomeId=s.IncomeId)
group by i.productId
My aim is result such as:
Id sum(income) sum(sales) now
1 200 50 150
2 150 20 130
3 120 100 20
but I get wrong sum(income):
Id sum(income) sum(sales) now
1 20000 50 19950
2 27500 20 27480
3 125000 100 12400
How can I write my SQL query to get this information?
For information, my database server is Interbase 2009.
thanks in advance!
I would use:
select incomecode,
sum(case when which = 'income' then sum_amt else 0 end) as sum_income,
sum(case when which = 'sales' then sum_amt else 0 end) as sum_sales,
sum(case when which = 'income' then sum_amt else 0 end)
- sum(case when which = 'sales' then sum_amt else 0 end) as diff_between
from (select incomecode, sum(quant) as sum_amt, 'income' as which
from income
group by incomecode
union all
select incomecode, sum(quant), 'sales'
from sales
group by incomecode) x
group by incomecode
It appears that Interbase does not support inline views and, in your version, does not yet support case statements. In that case try the following:
Step 1: Create a view for income totals
create view income_vw
select incomecode, sum(quant) as sum_amt
from income
group by incomecode
Step 2: Create a view for sales totals
create view sales_vw
select incomecode, sum(quant) as sum_amt
from sales
group by incomecode
Step 3: Run the final select query which will use those 2 views
select coalesce(i.incomecode, s.incomecode) as incomecode,
coalesce(i.sum_amt, 0) as sum_income,
coalesce(s.sum_amt, 0) as sum_sales,
coalesce(i.sum_amt, 0) - coalesce(s.sum_amt, 0) as diff_between
from income_vw i
full outer join sales_vw s
on i.incomecode = s.incomecode
I just neEd to get the total count of products by category and also the count of product which have price >= 5 by category
This is what I need
You can use my example ON SQL FIDDLE
Thank you very much!
select id_category,
count(1) as qty_products,
count(case when product_price>5 then 1 end) as [qty_products>5]
from orders
group by id_category
It should give you count of products per category. I'm not sure what do you want to display as "id_order" though ... If it's just row number, then you can do something like
select ROW_NUMBER() OVER(ORDER BY id_category) as rownum,id_category,
count(1) as qty_products,
count(case when product_price>5 then 1 end) as [qty_products>5]
from orders
group by id_category
All:
SELECT
id_category,
count(*) AS COUNT
FROM orders
GROUP BY id_category
Output:
ID_CATEGORY COUNT
1 12
2 10
5 6
Only price >=5:
SELECT
id_category,
COUNT(*) AS COUNT
FROM orders
WHERE product_price >=5
GROUP BY id_category
Output:
ID_CATEGORY COUNT
1 10
2 7
5 3
SELECT ID_CATEGORY, COUNT(ID_PRODUCT) AS NoOfProducts,
SUM(CASE WHEN PRODUCT_PRICE > 5 THEN 1 ELSE 0 END) AS ProductsAbove5
FROM Orders GROUP BY ID_CATEGORY
Fiddle here
However, you can not get Id_Order field as you have to group by category.
select
id_category,
count(id_product) as CountAll,
count(case when product_price > 5 then 1 else 0 end) as CountGreaterThan5
from orders
group by id_category
example: i need the numbers of which the sum of the amount where id = 1 is not equal of the sum of the amount where id <> 1, together with the id's and difference.
The table (view in this case) may look like this:
NUMBER AMOUNT ID
0001 500 1
0001 500 2
0002 400 3
0003 299 1
0003 300 3
0003 300 3
Many thanks for your help on this one.
Using this query you can get sum of amounts grouped by number, with ID equals 1 and ID not equals 1.
SELECT NUMBER
, SUM(CASE WHEN ID = 1 THEN AMOUNT ELSE 0 END) AS Equals1
, SUM(CASE WHEN ID <> 1 THEN AMOUNT ELSE 0 END) AS NotEquals1
FROM DataTable
GROUP BY NUMBER
If this is you expected, use following query to get difference amounts grouped by number.
SELECT NUMBER, (Equals1 - NotEquals1) AS DifferenceAmount
FROM
(
SELECT NUMBER
, SUM(CASE WHEN ID = 1 THEN AMOUNT ELSE 0 END) AS Equals1
, SUM(CASE WHEN ID <> 1 THEN AMOUNT ELSE 0 END) AS NotEquals1
FROM DataTable
GROUP BY NUMBER
) AS GroupedTable
WHERE Equals1 <> NotEquals1
SELECT
MyView.Number,
MyView.Amount - D.OverOneTotal AS Difference
FROM
MyView
LEFT JOIN
(SELECT
Number,
SUM(Amount) OverOneTotal
FROM
MyView
WHERE
ID > 1
GROUP BY
Number) D ON MyView.Number = D.Number
WHERE
MyView.ID = 1
AND MyView.Amount <> D.OverOneTotal