Oracle SELECT statement with GROUP BY and multiple WHERE clauses - sql

I'm having trouble with a select query. See sample data below. I want to select the Sku records where there is no stock quantity for warehouse id 1, but there is stock quantity for warehouse id 2 & 3. Not sure what I'm doing wrong?
SAMPLE TABLE DATA
Sku WhseId Qty
============================
ABC-123 1 6
ABC-123 2 3
ABC-123 3 2
XYZ-789 1 0
XYZ-789 2 1
XYZ-789 3 3
DEF-456 1 0
DEF-456 2 0
DEF-456 3 3
QUERY
SELECT Sku, WhseId, Qty
FROM PRODUCTS
WHERE (WhseId = 1 AND Qty < 1)
AND (WhseId = 2 AND Qty > 0
AND (WhseId = 3 AND Qty > 0)
GROUP BY Sku, WhseId, Qty
DESIRED RESULT
Sku WhseId Qty
============================
XYZ-789 1 0
XYZ-789 2 1
XYZ-789 3 3

I think you actually want to use EXISTS for this:
SELECT Sku, WhseId, Qty
FROM PRODUCTS p
WHERE EXISTS
(SELECT 1
FROM PRODUCTS p2
WHERE p.sku = p2.sku
AND p2.whseid = 1
AND p2.qty = 0)
AND EXISTS
(SELECT 1
FROM PRODUCTS p3
WHERE p.sku = p3.sku
AND p3.whseid = 2
AND p3.qty > 0)
AND EXISTS
(SELECT 1
FROM PRODUCTS p4
WHERE p.sku = p4.sku
AND p4.whseid = 3
AND p4.qty > 0)

Use aggregation and a having clause:
SELECT Sku
FROM PRODUCTS
GROUP BY Sku
HAVING SUM(CASE WHEN WhseId = 1 AND Qty < 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN WhseId = 2 AND Qty > 0 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN WhseId = 3 AND Qty > 0 THEN 1 ELSE 0 END) > 0;

SELECT
Sku,
WhseId,
Qty
FROM
(
SELECT
PRODUCTS.*,
SUM(CASE WHEN WhseId = 1 THEN Qty END) OVER (PARTITION BY Sku) AS whse1_qty,
SUM(CASE WHEN WhseId = 2 THEN Qty END) OVER (PARTITION BY Sku) AS whse2_qty,
SUM(CASE WHEN WhseId = 3 THEN Qty END) OVER (PARTITION BY Sku) AS whse3_qty
FROM
PRODUCTS
)
pivotted
WHERE
whse1_qty < 1
AND whse2_qty > 0
AND whse3_qty > 0

A simple solution:
select sku, whseid, qty
from etst
where sku = 'XYZ-789' and ((whseid = 1 and qty < 1)
or (whseid = 2 and qty > 0)
or (whseid = 3 and qty > 0))
group by sku, whseid, qty;

CREATE VIEW get_skus_2 AS
SELECT Sku
FROM PRODUCTS
WHERE Qty>0 AND WhseId=2;
CREATE VIEW get_skus_3 AS
SELECT Sku
FROM PRODUCTS
WHERE Qty>0 AND WhseId=3;
SELECT Sku s, WhseId, Qty
FROM get_skus_2 g2 JOIN get_skus_3 g3 ON g2.Sku = g3.Sku
WHERE s IN (
SELECT Sku
FROM PRODUCTS
WHERE Qty=0 AND WhseId=1);

How about combining IN with a GROUP BY on Sku?
SELECT Sku, WhseId, Qty
FROM PRODUCTS
WHERE Sku IN (
SELECT Sku
FROM PRODUCTS
WHERE WhseId IN (1, 2, 3)
GROUP BY Sku
HAVING MAX(CASE WHEN WhseId = 1 THEN Qty END) = 0
AND MAX(CASE WHEN WhseId = 2 THEN Qty END) > 0
AND MAX(CASE WHEN WhseId = 3 THEN Qty END) > 0
)
ORDER BY Sku, WhseId;
Result:
Sku WhseId Qty
------- ------ ---
XYZ-789 1 0
XYZ-789 2 1
XYZ-789 3 3
SQL Fiddle

Related

How to Replace NULL Value with 0 (Zero)?

I've just got myself stuck with some SQL query and I'm quite new on this.
I'm using pivot in my query.
This is my SELECT query:
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
and this is the output:
Domain | Fix Count
-------+-----------
1 1
2 1
4 2
5 1
And this is my query with PIVOT.
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
PIVOT
(SUM(slct.[Fix Count])
FOR slct.[Domain ID] IN ([1],[2],[3],[4],[5])
) AS pvt
This is the output:
1 | 2 | 3 | 4 | 5
1 1 NULL 2 1
Now my problem is how can I replace the NULL values with 0.
Just use conditional aggregation:
SELECT SUM(CASE WHEN Domain_Id = 1 THEN Fix_Count ELSE 0 END) as d_1,
SUM(CASE WHEN Domain_Id = 2 THEN Fix_Count ELSE 0 END) as d_2,
SUM(CASE WHEN Domain_Id = 3 THEN Fix_Count ELSE 0 END) as d_3,
SUM(CASE WHEN Domain_Id = 4 THEN Fix_Count ELSE 0 END) as d_4,
SUM(CASE WHEN Domain_Id = 5 THEN Fix_Count ELSE 0 END) as d_5
FROM (SELECT lg.domainNameID AS Domain_ID, COUNT(*) AS Fix_Count
FROM tbl_ATT_Request r JOIN
tbl_ATT_Login lg
ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID
) d

Group by to include case statement inside SQL statement

I have a table which stores purchase info from sellers and table contains rating to every purchase out of 5 stars. I want to have output Group By sellers and Each sellers good(Above 3) and bad(Below 4) ratings count
PurchaseId SellerId PurchaseRating
1 ABC 2
2 ABC 5
3 DEF 1
4 XYZ 2
5 DEF 4
7 ABC 3
OUTPUT
SellerId TotalOrders AvgRating Above3*(4&5) Below4*(1 to 3)
ABC 3 3.3 1 2
DEF 2 2.5 1 1
XYZ 1 2 0 1
For first 3 columns I am getting result using this
Select SellerId, Count(P.Id) TotalOrders, Avg(PurchaseRating) AvgRating,
CASE WHEN P.PurchaseRating >= 4 THEN 1 ELSE 0 END AS Above3*(4&5)
from
[dbo].[AmazonAbeOrdersPurchaseInfo] P
Where PurchaseRating is not null
Group by P.SellerId
order by TotalOrders desc
Unable to identify how to include case in group by clause.
You are trying to do conditional aggreation. The important thing is that you want the conditional expression inside the aggregate function:
select
sellerId,
count(*) totalOrders,
avg(purchaseRating) avgRating,
sum(case when purchaseRating > 3 then 1 else 0 end) above3,
sum(case when purchaseRating < 4 then 1 else 0 end) below4
from [dbo].[AmazonAbeOrdersPurchaseInfo]
where purchaseRating is not null
group by sellerId
order by totalOrders desc

How do I avoid duplicates when working out Aggregate functions on related tables?

It happens a lot that I need to make up some calculations on some related tables, to simplify the problem I created these 3 tables:
Transactions
TransactionId CustomerId SellerId NetValue Taxes
------------- ----------- ----------- ----------- -----------
1 1 2 435 10
TransactionDetails
TransactionDetailId TransactionId ItemId ItemPrice Qty IsNew IsOriginal
------------------- ------------- ----------- ----------- ----------- ----- ----------
1 1 1 40 8 1 1
2 1 2 50 2 0 1
3 1 3 15 1 1 0
Payments
PaymentId TransactionId PaymentValue PaymentMethodId
----------- ------------- ------------ ---------------
1 1 50 1
2 1 300 2
Now I need to get a one-row table like this:
NewItems NewItemsValue OriginalItems OriginalItemsValue ItemsValue TotalItems Paid Visa Cash
----------- ------------- ------------- ------------------ ----------- ----------- ----------- ----------- -----------
9 335 10 420 435 11 350 300 50
I first tried this (before applying SUM function):
SELECT
CASE WHEN Detail.IsNew = 1 THEN Detail.Qty END AS NewItems,
CASE WHEN Detail.IsNew = 1 THEN Detail.ItemPrice END AS NewItemsValue,
CASE WHEN Detail.IsOriginal = 1 THEN Detail.Qty END AS OriginalItems,
CASE WHEN Detail.IsOriginal = 1 THEN Detail.ItemPrice END AS OriginalItemsValue,
Pay.PaymentValue AS Paid,
Detail.ItemPrice AS ItemsValue,
Detail.Qty AS TotalItems,
(CASE WHEN Pay.PaymentMethodId=1 THEN Pay.PaymentValue ELSE 0 END) AS Cash,
(CASE WHEN Pay.PaymentMethodId=2 THEN Pay.PaymentValue ELSE 0 END) AS Visa
FROM Transactions AS Trans
JOIN TransactionDetails AS Detail ON Trans.TransactionId = Detail.TransactionId
JOIN Payments AS Pay
ON Trans.TransactionId = Pay.TransactionId
but there are duplicates. Now, my only option is to join the three tables with UNION but this seems a bad option, Is there a better solution to this?
You need aggregation but conditional :
SELECT SUM(CASE WHEN Detail.IsNew = 1 THEN Detail.Qty ELSE 0 END) AS NewItems,
SUM(CASE WHEN Detail.IsNew = 1 THEN Detail.ItemPrice ELSE 0 END) AS NewItemsValue,
SUM(CASE WHEN Detail.IsOriginal = 1 THEN Detail.Qty END) AS OriginalItems,
SUM(CASE WHEN Detail.IsOriginal = 1 THEN Detail.ItemPrice ELSE 0 END) AS OriginalItemsValue,
SUM(Pay.PaymentValue) AS Paid,
SUM(Detail.ItemPrice) AS ItemsValue,
SUM(Detail.Qty) AS TotalItems,
MAX(CASE WHEN Pay.PaymentMethodId=1 THEN Pay.PaymentValue END) AS Cash,
MAX(CASE WHEN Pay.PaymentMethodId=2 THEN Pay.PaymentValue END) AS Visa
FROM Transactions AS Trans JOIN
TransactionDetails AS Detail
ON Trans.TransactionId = Detail.TransactionId JOIN
Payments AS Pay
ON Trans.TransactionId = Pay.TransactionId
GROUP BY Trans.TransactionId;
Edit : You need aggregation before join :
select TransactionId,
sum(case when IsNew = 1 then qty else 0 end) as NewItems,
sum(case when IsNew = 1 then ItemPrice else 0 end) as NewItemsValue,
sum(case when IsOriginal = 1 then Qty else 0 end) as OriginalItems,
sum(case when IsOriginal = 1 then ItemPrice else 0 end) as OriginalItemsValue, count(*) as TotalItems, sum(PaymentValue) as paid
from TransactionDetails AS Detail
group by TransactionId;
I have to use sub-queries:
SELECT
(SELECT SUM(Qty) FROM TransactionDetails AS DetailNew WHERE DetailNew.TransactionId=Trans.TransactionId AND IsNew =1) AS NewItems,
(SELECT SUM(ItemPrice*Qty) FROM TransactionDetails AS DetailNew WHERE DetailNew.TransactionId=Trans.TransactionId AND IsNew =1) AS NewItemsValue,
(SELECT SUM(Qty) FROM TransactionDetails AS DetailOriginal WHERE DetailOriginal.TransactionId=Trans.TransactionId AND IsOriginal =1) AS OriginalItems,
(SELECT SUM(ItemPrice*Qty) FROM TransactionDetails AS DetailOriginal WHERE DetailOriginal.TransactionId=Trans.TransactionId AND IsOriginal =1) AS OriginalItemsValue,
(SELECT SUM(ItemPrice*Qty) FROM TransactionDetails AS Detail WHERE Detail.TransactionId=Trans.TransactionId) AS ItemsValue,
(SELECT SUM(Qty) FROM TransactionDetails) AS TotalItems,
(SELECT SUM(PaymentValue) FROM Payments) AS Paid,
(SELECT SUM(PaymentValue) FROM Payments WHERE PaymentMethodId=2) AS Visa,
(SELECT SUM(PaymentValue) FROM Payments WHERE PaymentMethodId=1) AS Cash
FROM Transactions AS Trans

Count in sql with group by

Table name: mytable
Id username pizza-id pizza-size Quantity order-time
--------------------------------------------------------------
1 xyz 2 9 2 09:00 10/08/2014
2 abc 1 11 3 17:45 13/07/2014
This is mytable which has 6 columns. Id is int, username is varchar, order-time is datetime and rest are of integer datatype.
How to count the number of orders with the following pizza quantities: 1, 2, 3, 4, 5, 6,7 and above 7?
Using a T-SQL query.
It would be very helpful If any one could help to me find the solution.
Try this !
SELECT COUNT(ID),CASE WHEN QUANTITY<7 THEN QUANTITY ELSE 'ABOVE7' END AS QUANTITIES
FROM mytable
GROUP BY CASE WHEN QUANTITY<7 THEN QUANTITY ELSE 'ABOVE7' END
Try
Select CASE WHEN Quantity > 7 THEN 'OVER7' ELSE Cast(quantity as varchar) END Quantity,
COUNT(ID) NoofOrders
from mytable
GROUP BY CASE WHEN Quantity > 7 THEN 'OVER7' ELSE Cast(quantity as varchar) END
or
Select
SUM(Case when Quantity = 1 then 1 else 0 end) Orders1,
SUM(Case when Quantity = 2 then 1 else 0 end) Orders2,
SUM(Case when Quantity = 3 then 1 else 0 end) Orders3,
SUM(Case when Quantity = 4 then 1 else 0 end) Orders4,
SUM(Case when Quantity = 5 then 1 else 0 end) Orders5,
SUM(Case when Quantity = 6 then 1 else 0 end) Orders6,
SUM(Case when Quantity = 7 then 1 else 0 end) Orders7,
SUM(Case when Quantity > 7 then 1 else 0 end) OrdersAbove7
from mytable
If the requirement is like count the number of orders with different pizza quantities and represent count of orders as : 1, 2, 3, 4, 5, 6,7 and consider all above order counts in new category : 'above 7' then you can use window function as:
select case when totalorders < = 7 then cast(totalorders as varchar(10))
else 'Above 7' end as totalorders
, Quantity
from
(
select distinct count(*) over (partition by Quantity order by Quantity asc)
as totalorders,
Quantity
from mytable
) T
order by Quantity
DEMO
Edit: if the requirement is like count the number of orders with pizza quantities: 1, 2, 3, 4, 5, 6,7 and consider all other pizza quantities in new category : 'above 7' then you can write as:
select distinct
count(*) over (
partition by Quantity order by Quantity asc
) as totalorders,
Quantity
from (
select
case when Quantity < = 7 then cast(Quantity as varchar(20))
else 'Above 7' end as Quantity, id
from mytable ) T
order by Quantity
DEMO

sql subquery that collects from 3 rows

I have a huge database with over 4 million rows that look like that:
Customer ID Shop
1 Asda
1 Sainsbury
1 Tesco
2 TEsco
2 Tesco
I need to count customers that within last 4 weeks had shopped in all 3 shops Tesco Sainsbury and Asda. Can you please advice if its possible to do it with subqueries?
This is an example of a "set-within-sets" subquery. You can solve it with aggregation:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
This structure is quite flexible. So if you wanted Asda and Tesco but not Sainsbury, then you would do:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) = 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
EDIT:
If you want a count, then use this as a subquery and count the results:
select count(*)
from (select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0
) t