Conditional sum in Group By query MSSQL - sql

I have a table OrderDetails with the following schema:
----------------------------------------------------------------
| OrderId | CopyCost | FullPrice | Price | PriceType |
----------------------------------------------------------------
| 16 | 50 | 100 | 50 | CopyCost |
----------------------------------------------------------------
| 16 | 50 | 100 | 100 | FullPrice |
----------------------------------------------------------------
| 16 | 50 | 100 | 50 | CopyCost |
----------------------------------------------------------------
| 16 | 50 | 100 | 50 | CopyCost |
----------------------------------------------------------------
I need a query that will surmise the above table into a new table with the following schema:
----------------------------------------------------------------
| OrderId | ItemCount | TotalCopyCost | TotalFullPrice |
----------------------------------------------------------------
| 16 | 4 | 150 | 100 |
----------------------------------------------------------------
Currently I am using a Group By on the Order.Id to the the item count. But I do not know how to conditionally surmise the CopyCost and FullPrice values.
Any help would be much appreciated.
Regards
Freddie

Try
SELECT OrderId,
COUNT(*) ItemCount,
SUM(CASE WHEN PriceType = 'CopyCost' THEN Price ELSE 0 END) TotalCopyCost,
SUM(CASE WHEN PriceType = 'FullPrice' THEN Price ELSE 0 END) TotalFullPrice
FROM OrderDetails
GROUP BY OrderId
SQLFiddle

Try this query
select
orderId,
count(*) as cnt,
sum(if(pricetype='CopyCost', CopyCost, 0)) as totalCopyCost,
sum(if(pricetype='FullPrice', FullPrice, 0)) as totalFullPrice
from
tbl
group by
orderId
SQL FIDDLE:
| ORDERID | CNT | TOTALCOPYCOST | TOTALFULLPRICE |
--------------------------------------------------
| 16 | 4 | 150 | 100 |

Could you use:
SELECT
OrderId,
Count(1) as ItemCount,
SUM(CASE WHEN PriceType = 'CopyCost'
THEN CopyCost ELSE 0 END) AS TotalCopyCost,
SUM(CASE WHEN PriceType = 'FullPrice'
THEN FullPrice ELSE 0 END) AS TotalFullPrice
FROM OrderDetails
GROUP BY OrderId

You could also try...
select A.OrderID, A.ItemCount,B.TotalCopyCost, C.TotalFullPrice
from (select OrderID, count(*) as ItemCount from orderdetails) as A,
(select OrderID, sum(CopyCost) as TotalCopyCost from orderdetails where PriceType = 'CopyCost') as B,
(select OrderID, sum(FullPrice) as TotalFullPrice from orderdetails where PriceType = 'FullPrice') as C
where A.OrderID = B.OrderID
SQLFiddle: http://sqlfiddle.com/#!2/946af/6

Related

Select orders which have the combination of product A and B

I'm trying to figure out a query that will get a list of orders which will have the combination of product A and B, C and D , X and Y, etc. So I need a list that will show only orders that have the combinations specified. Something like the code bellow:
select order_id, product_name
from orders
where product in( A & B, C & D)
group by order_id
Let me interpret your question as wanting specific pairs of products. If so, you can use group by and having. Assuming that there are no duplicates in your data:
select o.order_id
from orders o
group by o.order_id
having sum(case when o.product in ('A', 'B') then 1 else 0 end) = 2 or
sum(case when o.product in ('C', 'D') then 1 else 0 end) = 2 or
sum(case when o.product in ('X', 'Y') then 1 else 0 end) = 2;
If you want orders that have exactly two products and those pairs, then add the condition count(*) = 2. Or, if you want to be abstruse, you can change the else to something like else 10.
if your table data like
| ORDER_ID | PRODUCT_NAME | PRODUCT |
|----------|--------------|---------|
| 1 | xxx01 | A、B |
| 2 | xxx02 | B、C |
| 3 | xxx03 | C、D |
| 4 | xxx04 | D、X |
| 5 | xxx05 | E、Z |
try this script:
select order_id,product_name ,product
from orders
where product like ('%A%B%')
or product like ('%C%D%')
if your table data like ;
| ORDER_ID | PRODUCT_NAME | PRODUCT |
|----------|--------------|---------|
| 1 | xxx01 | A |
| 1 | xxx02 | B |
| 2 | xxx03 | C |
| 2 | xxx04 | D |
| 3 | xxx05 | E |
try this script:
select order_id
from orders
group by order_id having
count( case when product in ('A','B') then 1 else null end ) > 1
or
count( case when product in ('C','D') then 1 else null end ) > 1

SQL Server : display distinct list of orders, and indicate which orders contain specific products

I have a table that contains a lot of data, but the relevant data in the table looks something like this:
Orders table:
+----------+-----------+---------------+
| OrderID | Product | Date |
+----------+-----------+---------------+
| 1 | Apple | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Orange | 01/01/2001 |
| 1 | Pineapple | 01/01/2001 |
| 2 | Cherry | 02/02/2002 |
| 2 | Cherry | 02/02/2002 |
| 3 | Orange | 03/03/2003 |
| 3 | Apple | 03/03/2003 |
| 3 | Cherry | 03/03/2003 |
+----------+-----------+---------------+
I'd like a query to return a distinct list of orders, and if the order contains certain products, to indicate as such:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
Here's where I've left off and decided to seek out help:
WITH CTEOrder AS
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS OrderRN
FROM
Orders
)
CTEApple as
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS AppleRN
FROM
Orders
WHERE
Product = 'Apple'
),
CTEPear
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS PearRN
FROM
Orders
WHERE
Product = 'Pear'
)
SELECT
o.OrderID, o.Product, o.Date,
co.OrderRN, a.AppleRN, p.PearRN
FROM
Orders AS o
OUTER JOIN
CTEOrder AS co ON o.OrderID = co.Orderid
OUTER JOIN
CTEApple AS a ON o.OrderID = a.OrderID
OUTER JOIN
CTEPear AS p ON o.OrderID = p.OrderID
WHERE
(co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN = 1)
Currently my result set is unwieldy with a significant amount of duplication.
I'm thinking that I am heading in the wrong direction, but I don't know what other tools are available to me within SQL Server to cut up this data the way I need.
Thanks for any guidance!
Here's my result set after Nik Shenoy's guidance:
+----------+-----------+----------------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+----------------+
| 1 | 01/01/2001| x | NULL |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | NULL |
| 1 | 01/01/2001| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 3 | 03/03/2003| NULL | NULL |
| 3 | 03/03/2003| x | NULL |
| 3 | 03/03/2003| NULL | NULL |
+----------+-----------+----------------+
What is my next step to have only 1 row per Order:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
You can just use conditional aggregation:
select o.orderid, date,
max(case when product = 'Apple' then 'X' end) as IsApple,
max(case when product = 'Pear' then 'X' end) as IsPear
from orders o
group by o.orderid, date;
If you know all the products in advance, you can use the Transact-SQL PIVOT relational operator to cross-tabulate the data by product. If you use MAX or COUNT, you can just transform non-NULL or non-ZERO output to an 'x'
SELECT
PivotData.OrderID
, PivotData.OrderDate
, CASE WHEN PivotData.Apple IS NULL THEN '' ELSE 'X' END AS [Apple?]
, CASE WHEN PivotData.Pear IS NULL THEN '' ELSE 'X' END AS [Pear?]
, CASE WHEN PivotData.Orange IS NULL THEN '' ELSE 'X' END AS [Orange?]
, CASE WHEN PivotData.Pineapple IS NULL THEN '' ELSE 'X' END AS [Pineapple?]
, CASE WHEN PivotData.Cherry IS NULL THEN '' ELSE 'X' END AS [Cherry?]
FROM
(SELECT OrderID, Product, OrderDate) AS [Order]
PIVOT (MAX(Product) FOR Product IN ( [Apple], [Pear], [Orange], [Pineapple], [Cherry] )) AS PivotData

SQL sum of different status within 24 hrs group by hours

I am trying to sum status within 24 hour groups by hours. I have an order, order status and status table.
Order Table:
+---------+-------------------------+
| orderid | orderdate |
+---------+-------------------------+
| 1 | 2015-09-16 00:04:19.100 |
| 2 | 2015-09-16 00:01:19.490 |
| 3 | 2015-09-16 00:02:33.733 |
| 4 | 2015-09-16 00:03:58.800 |
| 5 | 2015-09-16 00:01:16.020 |
| 6 | 2015-09-16 00:01:16.677 |
| 7 | 2015-09-16 00:02:06.920 |
+---------+-------------------------+
Order Status Table:
+---------+----------+
| orderid | statusid |
+---------+----------+
| 1 | 11 |
| 2 | 22 |
| 3 | 22 |
| 4 | 11 |
| 5 | 22 |
| 6 | 33 |
| 7 | 11 |
+---------+----------+
Status Table:
+----------+----------+
| statusid | status |
+----------+----------+
| 11 | PVC |
| 22 | CCC |
| 33 | WWW |
| | |
+----------+----------+
I am try to write SQL that display the count of the status within 24 hours for distinct orderids grouped by hour like below:
+------+-----+-----+-----+
| Hour | PVC | CCC | WWW |
+------+-----+-----+-----+
| 1 | 0 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 0 | 0 |
| 4 | 1 | 0 | 0 |
+------+-----+-----+-----+
This is my SQL so far. I am stuck trying to get the sum of each order status:
SELECT
DATEPART(hour, o.orderdate) AS Hour,
SUM(
CASE (
SELECT stat.status
FROM Status stat, orderstatus os
WHERE stat.status IN ('PVC') AND os.orderid = o.id AND os.statusid = stat.id
)
WHEN 'PVC' THEN 1
ELSE 0
END
) AS PVC,
SUM(
CASE (
SELECT stat.status
FROM Status stat, orderstatus os
WHERE stat.status IN ('WWW') AND os.orderid = o.id AND os.statusid = stat.id
)
WHEN 'CCC' THEN 1
ELSE 0
END
) AS CCC,
SUM(
CASE (
SELECT stat.status
FROM Status stat, orderstatus os
WHERE stat.status IN ('CCC') AND os.orderid = o.id AND os.statusid = stat.id)
WHEN 'WWW' THEN 1
ELSE 0
END
) AS WWW
FROM orders o
WHERE o.orderdate BETWEEN DATEADD(d,-1,CURRENT_TIMESTAMP) AND CURRENT_TIMESTAMP
GROUP BY DATEPART(hour, o.orderdate)
ORDER BY DATEPART(hour, o.orderdate);
Here you go -- I'm ignoring the errors in your data since this will fail if the status table really had duplicate ids like in your example data.
SELECT hour, sum(PVC) as PVC, sum(CCC) as CCC, sum(WWW) as WWW
from (
select datepart(hour,orderdate) as hour,
case when s.status = 'PVC' then 1 else 0 end as PVC,
case when s.status = 'CCC' then 1 else 0 end as CCC,
case when s.status = 'WWW' then 1 else 0 end as WWW
from order o
join orderstatus os on o.orderid = os.orderid
join status s on s.statusid = os.statusid
) sub
group by hour
this should get you closer, then you have to pivot:
SELECT
DATEPART(HOUR,o.orderdate) AS orderDate_hour,
s.status,
COUNT(DISTINCT o.orderid) AS count_orderID
FROM
orders o INNER JOIN
orderstatus os ON
o.orderid = os.orderid INNER JOIN
status s ON
os.statusid = s.statusid
WHERE
o.orderdate >= DATEADD(d,-1,CURRENT_TIMESTAMP)
GROUP BY
DATEPART(HOUR,o.orderdate) , s.status
ORDER BY
DATEPART(HOUR,o.orderdate)
try this for the pivot:
SELECT
*
FROM
(SELECT
DATEPART(HOUR,o.orderdate) AS orderDate_hour,
s.status,
COUNT(DISTINCT o.orderid) AS count_orderID
FROM
orders o INNER JOIN
orderstatus os ON
o.orderid = os.orderid INNER JOIN
status s ON
os.statusid = s.statusid
WHERE
o.orderdate >= DATEADD(d,-1,CURRENT_TIMESTAMP)
GROUP BY
DATEPART(HOUR,o.orderdate) , s.status) s
PIVOT ( MAX(count_orderID) FOR status IN ('pvc','ccc','www')) AS p
ORDER BY
orderDate_hour

Sql Inner Join among 2 tables summing the qty field multiple times

I have two tables , A and B
Table A contains:
OrderNo | StyleNo | Qty
O-20 | S-15 | 20
O-20 | S-18 | 40
O-25 | S-19 | 50
Table B contains:
OrderNo | StyleNo | Ship Qty
O-20 | S-15 | 5
O-20 | S-18 | 30
O-20 | S-15 | 12
O-20 | S-18 | 6
Result Requires
OrderNo | StyleNo | Qty | Ship Qty
O-20 | S-15 | 20 | 17
O-20 | S-18 | 40 | 36
O-25 | S-19 | 50 | 0
The following query is not working
select
B.Orderno, B.StyleNo, sum(A.Qty), sum(B.QtyShip)
from
A
inner join
B on A.OrderNo = B.OrderNo and A.StyleNo = B.StyleNo
group by
B.OrderNo, B.StyleNo
The issue you're having is that it's summing the qty field multiple times. Move the sums to subqueries and use a join on those:
select a.orderno, a.styleno, a.qty, b.qtyship
from (
select orderno, styleno, sum(qty) qty
from a
group by orderno, styleno
) a
join (
select orderno, styleno, sum(qtyship) qtyship
from b
group by orderno, styleno
) b on a.orderno = b.orderno and a.styleno = b.styleno
SQL Fiddle Demo

Getting the whole row from grouped result

Here's a sample database table :
| ID | ProductID | DateChanged | Price
| 1 | 12 | 2011-11-11 | 93
| 2 | 2 | 2011-11-12 | 12
| 3 | 3 | 2011-11-13 | 25
| 4 | 4 | 2011-11-14 | 17
| 5 | 12 | 2011-11-15 | 97
Basically, what I want to happen is get the latest price of grouped by ProductID.
The result should be like this :
| ID | ProductID | Price
| 2 | 2 | 12
| 3 | 3 | 25
| 4 | 4 | 17
| 5 | 12 | 97
If you notice, the first row is not there because there is a new price for ProductID 12 which is the row of ID 5.
Basically, it should be something like get ID,ProductID and Price grouped by productID where DateChanged is the latest.
SELECT ID, ProductId, Price
FROM
(
SELECT ID, ProductId, Price
, ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY DateChanged DESC) AS rowNumber
FROM yourTable
) AS t
WHERE t.rowNumber = 1
SELECT ID, ProductID,DateChanged, Price
FROM myTable
WHERE ID IN
(
SELECT MAX(ID)
FROM myTable
GROUP BY ProductID
)
select a.id, a.productid, a.price
from mytable a,
(select productid, max(datechanged) as maxdatechanged
from mytable
group by productid) as b
where a.productid = b.productid and a.datechanged = b.maxdatechanged
SELECT ID, ProductId, Price
from myTable A
where DateChanged >= all
(select DateChanged
from myTable B
where B.ID = A.ID);