Query for Select Id,name,Cost on Delivery using sub query - sql

I want to make one query for selecting customer id, cus_name, Total COD Orders. I made these two queries ,but these queries calculating the COD and NON COD separately. How I make a single query using sub query.
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders'
FROM T_Acct_CompanyProfile c INNER JOIN
T_Inv_Order o
ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 1
GROUP BY o.cust_id, c.name;
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders'
FROM T_Acct_CompanyProfile c INNER JOIN
T_Inv_Order o
ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 0
GROUP BY o.cust_id, c.name;

With conditional aggregation:
SELECT o.cust_id,
UPPER(c.name),
SUM(CASE WHEN o.cod = 1 THEN 1 ELSE 0 END) AS 'Total COD Orders',
SUM(CASE WHEN o.cod = 0 THEN 1 ELSE 0 END) AS 'Total non COD Orders'
FROM T_Acct_CompanyProfile c
INNER JOIN T_Inv_Order o ON c.id = o.cust_id
WHERE c.type_id = 1
group by o.cust_id, c.name

As the queries seem to be very similar you could use a union. So your queries become:
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders' FROM T_Acct_CompanyProfile c
INNER JOIN T_Inv_Order o ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 1
group by o.cust_id, c.name
UNION
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders' FROM T_Acct_CompanyProfile c
INNER JOIN T_Inv_Order o ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 0
group by o.cust_id, c.name
This will comebine the two queries into one results set. See http://www.w3schools.com/sql/sql_union.asp for more information on the Union keyword.

Just use conditional aggregation:
SELECT o.cust_id, UPPER(c.name),
SUM(CASE WHEN o.cod = 1 THEN 1 ELSE 0 END) as TotalCODOrders,
SUM(CASE WHEN o.cod = 0 THEN 1 ELSE 0 END) as TotalNonCODOrders
FROM T_Acct_CompanyProfile c INNER JOIN
T_Inv_Order o
ON c.id = o.cust_id
WHERE c.type_id = 1
GROUP BY o.cust_id, c.name;
If o.cod only takes on the values 0 and 1, then you can use the short-cut:
SELECT o.cust_id, UPPER(c.name),
SUM(o.cod = 1) as TotalCODOrders,
SUM(1 - o.cod) as TotalNonCODOrders,

Use Union
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders'
FROM T_Acct_CompanyProfile c INNER JOIN
T_Inv_Order o
ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 1
GROUP BY o.cust_id, c.name;
UNION
SELECT o.cust_id, UPPER(c.name), count(o.order_no) AS 'Total COD Orders'
FROM T_Acct_CompanyProfile c INNER JOIN
T_Inv_Order o
ON c.id = o.cust_id
WHERE c.type_id = 1 AND o.cod = 0
GROUP BY o.cust_id, c.name;

Related

SQL is it possible to have multiple subqueries in From clause?

So I have 2 tables.
the first one would be the bill:
id
total
client_code
created_at
1
10
1
2022-02-01
2
20
1
2022-02-01
3
20
3
2022-03-01
the second would be the product (for a bill):
bill_id
category
total
1
Electronic
2
1
Food
5
1
Food
3
2
Food
10
2
Food
10
3
Food
10
3
Food
10
What I want to get with my query is the average spending by a client for each month over the last 4 months for example.
my query right now is looking like this for the first 2 months and it works, I get the result I want:
SELECT
COALESCE(AVG(first.total),0),
COALESCE(AVG(second.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as first),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as second)
but as soon as I get to 3 months it doesn't anymore:
SELECT
COALESCE(AVG(first.total),0),
COALESCE(AVG(second.total),0),
COALESCE(AVG(third.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as first),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as second),
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-03-01' AND b.created_at < '2022-01-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as three)
Is there a rule that keeps me from having more than two subqueries inside my from clause?
If so is there an other way of doing this? My real problem is for 12 month actually not 4. I already have a working solution but performance wise it's bad, that is why I am trying this.
My working solution is looking like that:
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-02-01' AND b.created_at < '2022-03-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-03-01' AND b.created_at < '2022-04-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-04-01' AND b.created_at < '2022-05-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
UNION ALL
(SELECT
COALESCE(AVG(req.total),0)
FROM
(SELECT
SUM(p.total) as total
FROM
bill b
INNER JOIN product p one b.id = p.bill_id
WHERE
b.created_at >= '2022-05-01' AND b.created_at < '2022-06-01' AND
p.category = 'FOOD'
GROUP BY
p.client_code) as req)
Something like this (but I'm pretty sure I don't have the right way of getting months, years in postgres - you will have to fix that!)
SELECT
A.client_code,
avg(A.total) as AvgMonthlyTotal
FROM
(
SELECT
SUM(p.total) as total,
month(b.created_at),
p.client_code
FROM
bill b
INNER JOIN product p
on b.id = p.bill_id
WHERE
p.Category = 'FOOD'
and year(b.created_at) = 2022
GROUP BY
month(b.created_at)
p.client_code
) A
GROUP BY A.client_code;

SQL JOIN omits some fields

I have the following tables:
Product_T with columns:
ProductID,
ProductDescription
OrderLine_T with columns:
OrderID,
ProductID,
OrderedQuantity
Order_T with columns:
OrderID,
CustomerID,
Customer_T with columns:
CustomerID,
CustomerName
I want to list the product ID and description, along with the customer ID and name for the customer who has bought the most of that product and also show the total quantity ordered by that customer.
I came up with following query, to list the max quantity product per order:
SELECT o1.OrderID, o1.ProductID, SUM(o1.OrderedQuantity) AS A
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
HAVING SUM(o1.OrderedQuantity) = (
SELECT MAX(s.d)
FROM (
SELECT
o1.OrderID,
o1.ProductID,
SUM(o1.OrderedQuantity) AS d
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
) s
WHERE o1.ProductID = s.ProductID
)
And that gave me a correct output of:
50 20 1
48 17 5
32 14 10
59 13 2
1 10 9
2 8 2
69 7 4
4 6 3
32 5 10
55 4 2
2 3 12
1 2 18
26 1 5
But then, when I tried joining it with other tables, so I could select CustomerName and CustomerID, like so:
SELECT
o1.ProductID,
s.CustomerName,
s.CustomerID,
SUM(o1.OrderedQuantity) AS A
FROM OrderLine_T o1
INNER JOIN (
SELECT
c1.CustomerName,
c1.CustomerID,
p1.ProductID
FROM Product_T p1
INNER JOIN OrderLine_T o3 ON p1.ProductID = o3.ProductID
INNER JOIN Order_T o2 ON o3.OrderID = o2.OrderID
INNER JOIN Customer_T c1 ON o2.CustomerID = c1.CustomerID
) s ON s.ProductID = o1.ProductID
GROUP BY
o1.ProductID,
s.CustomerName,
s.CustomerID
HAVING SUM(o1.OrderedQuantity) = (
SELECT MAX(s.d)
FROM (
SELECT
o1.OrderID,
o1.ProductID,
SUM(o1.OrderedQuantity) AS d
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
) s
WHERE o1.ProductID = s.ProductID
) ;
The output shrunk to:
17 Contemporary Casuals 1 5
8 Home Furnishings 3 2
7 Eastern Furniture 4 4
10 Eastern Furniture 4 9
20 Dunkins Furniture 8 1
13 Ikards 13 2
Why could that be?
It seems you should be using window functions here, such as ROW_NUMBER, along with conditional aggregation
SELECT
o.ProductID,
p.Description,
CustomerID = MAX(CASE WHEN o.rn = 1 THEN c.CustomerID END),
CustomerName = MAX(CASE WHEN o.rn = 1 THEN c.CustomerName END),
SUM(CASE WHEN o.rn = 1 THEN o.TotalQty END) AS QtyForTopCustomer
SUM(o.TotalQty) AS TotalQty
FROM (
SELECT
o.ProductID,
o.CustomerID,
TotalQty = SUM(oi.OrderedQuantity),
rn = ROW_NUMBER() OVER (PARTITION BY oi.ProductId ORDER BY SUM(oi.OrderedQuantity) DESC)
FROM OrderLine_T ol
INNER JOIN Order_T o ON o.OrderID = ol.OrderID
GROUP BY
o.ProductID,
o.CustomerID
) o
INNER JOIN Customer_T c ON c.CustomerID = o.CustomerID
INNER JOIN Product_T p ON p.ProductID = ol.ProductID
GROUP BY
o.ProductID,
p.Description;
If you only wanted the data for that one customer, you could remove the conditional aggregation and just filter by row-number
SELECT
o.ProductID,
p.Description,
o.CustomerID,
o.CustomerName,
o.TotalQty
FROM (
SELECT
p.ProductID,
p.Description,
o.CustomerID,
TotalQty = SUM(oi.OrderedQuantity),
rn = ROW_NUMBER() OVER (PARTITION BY oi.ProductId ORDER BY SUM(oi.OrderedQuantity) DESC)
FROM OrderLine_T ol
INNER JOIN Order_T o ON o.OrderID = ol.OrderID
GROUP BY
p.ProductID,
p.Description,
o.CustomerID
) o
INNER JOIN Customer_T c ON c.CustomerID = o.CustomerID
INNER JOIN Product_T p ON p.ProductID = ol.ProductID
WHERE o.rn = 1;

SQL query that summarizes each group

I need help with this. I need to summarize each group of tests. The result should look like this:
name_of_the_group | all_test_cases | passed_test_cases | total_value
numerical stability 4 4 80
memory usage 3 2 20
corner cases 0 0 0
performance 2 0 0
columns:
name_of_the_group (all groups need to be displayed even if there are no corresponding tests)
all_test_cases (number of tests in the group)
passed_test_cases (number of test cases with the status OK)
total_value (total value of passed tests in this group)
Here is the fiddle example: http://sqlfiddle.com/#!17/e96109
Below SQL query provide a solution to all test cases
When there is a name entry in table test_groups but missing in test_cases.
Order by test_value desc first and then by name lexicographically increasing
order.
SELECT CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END AS name,
count(c.group_name) AS all_test_cases,
count(CASE
WHEN c.status = 'OK' THEN 1
END) AS all_test_cases,
sum(CASE
WHEN c.status = 'OK' THEN g.test_value
ELSE 0
END) AS total_value
FROM test_groups g
LEFT JOIN test_cases c ON g.name = c.group_name
GROUP BY CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END
ORDER BY total_value DESC,
CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END
simple conditional aggregation:
select
t.name,
count(*) as total,
sum(case when c.status = 'OK' then 1 else 0 end) as passed,
sum(case when c.status = 'OK' then test_value end) as score
from
test_groups t
left join test_cases c
on t.name = c.group_name
group by t.name
Fiddle
with all_test_cases as (select name, count(id) as cnt from (
select a.name, a.test_value, b.id, b.status from test_groups a
left join test_cases b on a.name = b.group_name) total_test
group by name),
passed_test_cases as (
select name, count(name) as cnt from (
select a.name, a.test_value, b.id, b.status from test_groups a
join test_cases b on a.name = b.group_name
and b.status = 'OK') OK_test
group by name)
select tg.name, atc.cnt as all_test_cases , coalesce(ptc.cnt, 0) as passed_test_cases, coalesce(ptc.cnt * tg.test_value, 0) as total_value
from test_groups tg
left join all_test_cases atc on tg.name = atc.name
left join passed_test_cases ptc on tg.name = ptc.name
order by total_value desc, name
Select t.name,
sum(case when c.status = 'OK' or c.status = 'ERROR' then 1 else 0 end) as all_test_cases,
sum(case when c.status = 'OK' then 1 else 0 end) as passed_test_cases,
sum(case when c.status = 'OK' then 1 else 0 end) * t.test_value as total_value
from
test_groups t
left join test_cases c
on t.name = c.group_name
group by t.name,t.test_value
order by ,t.total_value,t.name
WITH A AS (SELECT group_name AS name, COUNT(status) AS all_test_cases,
SUM(CASE
WHEN status = 'OK' THEN 1
ELSE 0
END) AS passed_test_cases
FROM test_cases
GROUP BY group_name)
SELECT g.name, IFNULL(c.all_test_cases,0),
IFNULL(c.passed_test_cases,0),
IFNULL(g.test_value*c.passed_test_cases,0) AS total_value
FROM test_groups AS g
LEFT JOIN A AS c
ON g.name = c.name
GROUP BY g.name
ORDER BY total_value DESC;
Select Name Name_Of_The_Group ,Count(Status) All_Test_Cases,
Sum(Case When Status = 'OK' Then 1 Else 0 End) passed_test_cases ,
Sum(Case When Status = 'OK' Then test_value End) total_value
From Test_Cases Full Outer Join Test_Groups On Name = Group_Name
Group By Group_Name,Name
order by Name_Of_The_Group;
Try the following query:
select t.name,
count(c.*)as total,
sum(case when c.status='OK'then 1 else 0 end) as passed,
Case when sum(case when c.status='OK'then test_value end)Is NULL then 0
Else sum(case when c.status='OK'then test_value end)End as total_value
from test_groups t
left join test_cases c on t.name=c.group_name
group by t.name
order by total_value desc,t.name;

sql query with percent and aggregation functions

I'm trying to write a sql query where I need number of scores > 3 by county, then for counties on that list I need to produce a percentage of rooms with scores < 3. So I need three columns, County Name, # of scores > 3 by county, % of rooms with scores < 3 by county
SELECT County = c.Description, [Score > 3] = count(s.Score),
((select count(room.Name) where s.Score< 3) /( select count(room.Name) ) * 100)
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
where s.Score > 3
Group By c.Description
I think you are over complicating the issue, rather than subselects you can just limit the data returned by using the HAVING clause, then use CASE in the COUNT:
SELECT County = c.Description,
[Score > 3] = COUNT(CASE WHEN Sites.Score > 3 THEN 1 END),
[% Score < 3] = 100.0 * COUNT(CASE WHEN Sites.Score < 3 THEN 1 END) / COUNT(1)
FROM Sites
INNER JOIN Profiles
ON Sites.Profile_Id = Profiles.Id
INNER JOIN Counties
ON Profiles.County_Id = Counties.Id
INNER JOIN Rooms
ON Sites.Id = Rooms.Site_Id
GROUP BY c.Description
HAVING COUNT(CASE WHEN Sites.Score > 3 THEN 1 END) > 0;
Demo on SQL Fiddle
EDIT
SELECT County = c.Description,
[Score > 3] = COUNT(CASE WHEN Sites.Score > 3 THEN 1 END),
[% Score < 3] = 100.0 * SUM(CASE WHEN Sites.Score < 3 THEN 1 END) / COUNT(*),
[Score > 3] = SUM(CASE WHEN Sites.Score > 3 THEN RoomCount ELSE 0 END),
[% Score < 3] = 100.0 * SUM(CASE WHEN Sites.Score < 3 THEN RoomCount ELSE 0 END) / SUM(RoomCount)
FROM Sites
INNER JOIN Profiles
ON Sites.Profile_Id = Profiles.Id
INNER JOIN Counties
ON Profiles.County_Id = Counties.Id
INNER JOIN
( SELECT Site_Id, RoomCount = COUNT(*)
FROM Rooms
GROUP BY Site_Id
) Rooms
ON Sites.Id = Rooms.Site_Id
GROUP BY c.Description
HAVING COUNT(CASE WHEN Sites.Score > 3 THEN 1 END) > 0;
Use Cast
SELECT County = c.Description, [Score > 3] = count(s.Score),
( Cast(select count(room.Name) where s.Score < 3 ) as float / ( select count(room.Name) ) * 100)
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
where s.Score > 3
Group By c.Description
[Score > 3] = count(s.Score)
...
where s.Score > 3
Both statements are not needed. In fact your Where clause limits all operations to s.score >3, which is not ideal when you are also trying to pull data from scores <3.
If you are trying to count both the cases where s.Score>3 and where it is <3, you need to use the CASE statement
SELECT SUM(CASE WHEN s.score < 3 THEN 1 ELSE 0 END) AS Hiscores,
SUM(CASE WHEN s.score > 3 THEN 1 ELSE 0 END) /count(s.scores) AS percentLowScores
This should do it
SELECT c.Description County,
SUM(CASE WHEN s.score < 3 THEN 1 ELSE 0 END) AS Hiscores,
SUM(CASE WHEN s.score > 3 THEN 1 ELSE 0 END) /count(s.scores) AS percentLowScores
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
Group By c.Description

SUM data only when COUNT(*) > 0

Here is what I have so far...
SELECT pr.[name], SUM(COALESCE(qm.iscompleted,0)) count_yes,
SUM(COALESCE(qm.iscompleted,1)) count_no,
COUNT(p.id) AS tot
FROM (aco.practices pr
left JOIN aco.patients_practices ppr ON (ppr.practiceid = pr.id)
left JOIN aco.patients p ON (p.id = ppr.patientid)
LEFT JOIN aco.qmheader qm ON (qm.patientid = p.id AND qm.practiceid = pr.id)
)
WHERE (pr.parentaco = 30982)
GROUP BY pr.[name]
ORDER BY pr.[name]
Here are some results
name count_yes count_no tot
name1 0 1 0
name2 0 1 0
name3 0 273 273
name4 0 114 114
For the most part it is correct, the only thing I need is to not SUM on the count_no if the tot is 0.
Is there a way to do this easily?
Thanks.
Change your select clause to:
SELECT pr.[name],
SUM(COALESCE(qm.iscompleted,0)) AS count_yes,
(case when COUNT(p.id) > 0 then
SUM(COALESCE(qm.iscompleted,1))
else
0 end) AS count_no,
COUNT(p.id) AS tot
Use the HAVING clause:
SELECT pr.[name], SUM(COALESCE(qm.iscompleted,0)) count_yes,
SUM(COALESCE(qm.iscompleted,1)) count_no,
COUNT(p.id) AS tot
FROM (aco.practices pr
left JOIN aco.patients_practices ppr ON (ppr.practiceid = pr.id)
left JOIN aco.patients p ON (p.id = ppr.patientid)
LEFT JOIN aco.qmheader qm ON (qm.patientid = p.id AND qm.practiceid = pr.id)
)
WHERE (pr.parentaco = 30982)
GROUP BY pr.[name]
HAVING COUNT(p.id) = 0
ORDER BY pr.[name]