SQL Total Sale calculation - sql

I have a sale table includes purchases/returns/exchanges
Sample:
--------**saleTbl**------------
CustID | DOP | SKU | Price
111 | 11/05/12 | 001 | 45.99
222 | 11/20/12 | 001 | 45.99
111 | 11/06/12 | 002 | 40.95
111 | 11/06/12 | 001 | -45.99
111 | 11/19/12 | 004 | 50.00
222 | 11/25/12 | 003 | 20.99
111 | 12/01/12 | 002 | -40.95
111 | 12/01/12 | 003 | 20.99
Criteria is: find total for each customer during 11/05/12 - 11/20/12. If customer exchanged the item that was purchased during that time and purchase with the same day will be count.
The expected result is:
CustID | DOP | Price
222 | 11/20/12 | 45.99
111 | 12/01/12 | 70.99
I have tried to get the total but of course it is not right:
SELECT DISTINCT [num_cp] AS 'Member Id'
,MAX([dop]) AS 'Date'
,SUM([price]) AS 'Point'
FROM [Mailing_List].[dbo].[UGG_DoublePoint]
WHERE [num_cp] IN
(
SELECT [num_cp]
FROM [Mailing_List].[dbo].[UGG_DoublePoint]
GROUP BY [num_cp]
HAVING SUM([price]) >0
)
--AND
AND [dop] BETWEEN '11/05/12' AND '11/20/12'
GROUP BY [num_cp]
Please help! Thanks everyone.

I think you need to change your query to this
; WITH CTE AS
(
SELECT *,
COUNT(*) OVER (PARTITION BY CustID, DOP) Row_Cnt
FROM TEST
), CTE2 AS
(
SELECT * FROM CTE
WHERE [dop] BETWEEN '11/05/12' AND '11/20/12'
), CTE3 AS
(
SELECT * FROM CTE2 WHERE price > 0
UNION
SELECT * FROM CTE2 WHERE price < 0
and SKU IN (SELECT SKU FROM CTE2 WHERE Price > 0)
UNION
SELECT * FROM CTE
WHERE row_cnt > 1 and DOP IN (
SELECT max(A.dop) d FROM CTE A
INNER JOIN CTE2 B ON A.CustID = B.CustID AND A.SKU = B.SKU
)
)
SELECT Custid, max(dop) dateid, sum(price) Price
from cte3
group by custid;
Check SQL Fiddle Demo

I think this will work. It basically filters out any returns from the result set by use of a left join.
NOTE: There would be an issue with this in the case that someone purchased/returned multiple SKUs of the same thing on the same day.
select pur.CustId, sum(pur.price) TotalPrice
from test pur
left join test ret
on pur.custid = ret.custid
and pur.dop = ret.dop
and pur.sku = ret.sku
and pur.price = (-1 * ret.price)
where pur.dop between '11/05/2012' AND '11/20/2012'
and ret.price is null
group by pur.CustId

Related

Join with union returns me wrong result

I need to use union all twice. And then to join them with the same table. First union will contain a where statement and the second it will not.
Problem is that when I use the second table my result is changing.
select sum(x.quantity*x.Price)
from CustomerTrans t1
inner join (
select *
from InventoryTrans
union all
select *
from InventoryTransTemp
) x on t1.TrnDocumentID = x.TrnDocumentID
group by t1.TrnDocumentID
Here is the output from this result
First Result
Then I am adding the second union with the where statement inside
select sum(x.quantity*x.Price), sum(x2.quantity*x2.Price)
from CustomerTrans t1
left join (
select *
from InventoryTrans
union all
select *
from InventoryTransTemp
) x on t1.TrnDocumentID = x.TrnDocumentID
left join (
select *
from InventoryTrans
where printed = 2 or InvoicePaymentID = 2
union all
select *
from InventoryTransTemp
where printed = 2 or InvoicePaymentID = 2
) x2 on t1.TrnDocumentID = x2.TrnDocumentID
group by t1.TrnDocumentID
Here is the second result
Second Result
Second result it should be 3.80 and not 7.60
It look like it multiples my price *2 instead *1.
What happens here is that you join rows you don't want to join. Let's say your first subquery returns
+----------+-------+
| quantity | price |
+----------+-------+
| 10 | 100 |
| 10 | 200 |
+----------+-------+
for a particular document ID. And your second subquery returns only
+----------+-------+
| quantity | price |
+----------+-------+
| 10 | 200 |
+----------+-------+
The joined result is:
+------------+---------+-------------+----------+
| x.quantity | x.price | x2.quantity | x2.price |
+------------+---------+-------------+----------+
| 10 | 100 | 10 | 200 |
| 10 | 200 | 10 | 200 |
+------------+---------+-------------+----------+
And the aggregations results thereafter are:
+----------+-----------+
| x_result | x2_result |
+----------+-----------+
| 3000 | 4000 |
+----------+-----------+
instead of
+----------+-----------+
| x_result | x2_result |
+----------+-----------+
| 3000 | 2000 |
+----------+-----------+
Instead of joining single rows, you want to join aggregation results (the totals per document):
select
ct.*,
coalesce(u1.total, 0) as u1_total,
coalesce(u2.total, 0) as u2_total
from customertrans ct
left join
(
select trndocumentid, sum(quantity * price) as total
from
(
select * from inventorytrans
union all
select * from inventorytranstemp
) union1
group by trndocumentid
) u1 on u1.trndocumentid = ct.trndocumentid
left join
(
select trndocumentid, sum(quantity * price) as total
from
(
select * from inventorytrans where printed = 2 or invoicepaymentid = 2
union all
select * from inventorytranstemp where printed = 2 or invoicepaymentid = 2
) union2
group by trndocumentid
) u2 on u2.trndocumentid = ct.trndocumentid
group by ct.trndocumentid
order by ct.trndocumentid;

Select Top 20 Distinct Rows in Each Category

I have a database table in the following format.
Product | Date | Score
A | 01/01/18 | 99
B | 01/01/18 | 98
C | 01/01/18 | 97
--------------------------
A | 02/01/18 | 99
B | 02/01/18 | 98
C | 02/01/18 | 97
--------------------------
D | 03/01/18 | 99
A | 03/01/18 | 98
B | 03/01/18 | 97
C | 03/01/18 | 96
I want to pick the first from every month such that there are no repeat products. For example, the output of the above table should be
Product | Date | Score
A | 01/01/18 | 99
B | 02/01/18 | 98
D | 03/01/18 | 99
How do I get this result with a single sql query? The actual table is much bigger than this and I want top 20 from every month without repetition.
This is a hard problem -- a type of subgraph problem that isn't really suitable to SQL. There is a brute force approach:
with jan as (
select *
from t
where date = '2018-01-01'
limit 1
),
feb as (
select *
from t
where date = '2018-02-01' and
product not in (select product from jan)
),
mar as (
select *
from t
where date = '2018-03-01' and
product not in (select product from jan) and
product not in (select product from feb)
)
select *
from jan
union all
select *
from feb
union all
select *
from mar;
You can generalize this with additional CTEs. But there is no guarantee that a month will have a product -- even when it could have had one.
It is possible by using row_number.
select * from (
select row_Number() over(partition by Product order by Product ) as rno,* from
Products
) as t where t.rno<=20
I think you want top 20 records every month without repeating products than below solution will be work.
select *
into #temp
from
(values
('A','01/01/18','99')
,('B','01/01/18','98')
,('C','01/01/18','97')
,('A','02/01/18','99')
,('B','02/01/18','98')
,('C','02/01/18','97')
,('D','03/01/18','99')
,('A','03/01/18','98')
,('B','03/01/18','97')
,('C','03/01/18','96')
) AS VTE (Product ,Date, Score )
select * from
(
select * , ROW_NUMBER() over (partition by date,product order by score ) as rn
from #TEMP
)
A where rn < 20

SQL Server Query to find records with aggregate funct on one column but multiple columns in select clause

Here is the minimized version of the Customer table. There can be customers having same account number mapped to different Group . I am looking to find out customer numbers which are mapped to more than one group. As I was using sybase my query below was working fine. Same query does not work in SQL Server.
Can I get both custAccnt and corresponding custId in one query as below.
select DISTINCT lt.custAccnt, lt.custId from VAL_CUSTOMERS lt
where lt.eligible = 'Y' group by lt.custAccnt
having count(distinct lt.custId) > 1
+----------+-----------+---------+----------+
| custName | custAccnt | custId | eligible |
+----------+-----------+---------+----------+
| Joe | AB1VU1235 | 43553 | Y |
| Joe | AB1VU1235 | 525577 | Y |
| Lucy | CDNMY4568 | 332875 | Y |
| Lucy | CDNMY4568 | 211574 | Y |
| Lucy | CDNMY4568 | 211345 | Y |
| Manie | TZMM7S009 | 123890 | Y |
| Tom | YFDU1235 | 1928347 | Y |
| Tom | YFDU1235 | 204183 | Y |
| Chef | TNOTE6573 | 734265 | Y |
+----------+-----------+---------+----------+
Result :-
+-----------+---------+
| AB1VU1235 | 43553 |
| AB1VU1235 | 525577 |
| CDNMY4568 | 332875 |
| CDNMY4568 | 211574 |
| CDNMY4568 | 211345 |
| YFDU1235 | 1928347 |
| YFDU1235 | 204183 |
+-----------+---------+
There are many ways to tackle this. Here are a couple of them that should work.
select lt.custAccnt
, lt.custId
from VAL_CUSTOMERS lt
cross apply
(
select c.custAccnt
from VAL_CUSTOMERS c
where c.custAccnt = lt.custAccnt
group by c.custAccnt
having count(*) > 1
) x
where lt.eligible = 'Y'
select lt.custAccnt
, lt.custId
from VAL_CUSTOMERS lt
where lt.eligible = 'Y'
AND lt.custAccnt IN
(
select c.custAccnt
from VAL_CUSTOMERS c
group by c.custAccnt
having count(*) > 1
)
In case of duplicates custAccnt and custId in the table, #Sean query won't work.
WITH cte AS(SELECT *
, COUNT (custId) OVER (PARTITION BY custAccnt) AS CntcustId
, ROW_NUMBER () OVER (PARTITION BY custAccnt, custId ORDER BY custName) AS Rownum
FROM VAL_CUSTOMERS
WHERE eligible = 'Y'
)
SELECT custAccnt, custId
FROM cte
WHERE CntcustId>1
AND Rownum = 1;
Using row number to eliminate the duplicates.
I think this might work...
"...customer numbers which are mapped to more than one group..." , <-- group is custAcct?
select t.custAccnt, t.custId
from VAL_CUSTOMERS t
where (Select count(distinct custAccnt )
from VAL_CUSTOMERS
Where custId = t.custId) > 1
The statement "...customer numbers which are mapped to more than one group..." does not say anything about "eligibility", so I did not mention it. If you really meant to say:
"...eligible customer numbers which are mapped to more than one group...", then try this:
select t.custAccnt, t.custId
from VAL_CUSTOMERS t
where eligible = 'Y'
and (Select count(distinct custAccnt )
from VAL_CUSTOMERS
Where custId = t.custId) > 1
or, this might be faster... it answers a slightly different, but, (I think) equivalent question,
"find ...eligible customer numbers where there is another row for the same customer number mapped to a different custAccnt ..."
select t.custAccnt, t.custId
from VAL_CUSTOMERS t
where eligible = 'Y'
and exists
(Select * from VAL_CUSTOMERS
Where custId = t.custId
and custAccnt != t.custAccnt )
;WITH cte1
( custName , custAccnt , custId , eligible )
As
(
SELECT 'Joe' ,'AB1VU1235' , 43553 , 'Y' UNION ALL
SELECT 'Joe' ,'AB1VU1235' , 525577 , 'Y' UNION ALL
SELECT 'Lucy' ,'CDNMY4568' , 332875 , 'Y' UNION ALL
SELECT 'Lucy' ,'CDNMY4568' , 211574 , 'Y' UNION ALL
SELECT 'Lucy' , 'CDNMY4568' , 211345 , 'Y' UNION ALL
SELECT 'Manie' ,'TZMM7S009' , 123890 , 'Y' UNION ALL
SELECT 'Tom' ,'YFDU1235' , 1928347 , 'Y' UNION ALL
SELECT 'Tom' ,'YFDU1235' , 204183 , 'Y' UNION ALL
SELECT 'Chef' ,'TNOTE6573' , 734265 , 'Y'
)
,cte2 AS (
SELECT custName
,custAccnt
,count(custName) cnt
FROM cte1
GROUP BY custName,custAccnt
)
,cte3 AS (
SELECT custName
,cnt
FROM cte2 WHERE cnt <> 1
)
SELECT custAccnt
,custId
FROM cte1
WHERE custName IN (
SELECT custName
FROM cte3
)

Count occurence of specific code per customer in 6 month period

I have a table that contains the following:
customerid | date (dmy) | productid
John | 1-3-14 | A
John | 7-5-14 | Y
John | 8-5-14 | Y
John | 1-10-15 | B
John | 1-11-15 | Y
Pete | 1-7-15 | Y
I need to find out how often customer X has bought Product Y in a six-month period.
The start of a period is defined as the first time a customer has bought one of the products A,B, C or Y. The endtime of a period is exactly six months after that.
The next period starts when the customer buys again one of the products A,B,C or Y.
So the output should be
customerid | period-start | period-end | countofY
John | 1-3-14 | 8-5-14 | 2
John | 1-10-15 | 1-11-15 | 1
Pete | 1-7-15 | 1-7-15 | 1
SELECT c.Customerid, MIN(c.pdate) AS startperiod, c1.endperiod,
(
SELECT COUNT(temp.productid) FROM Customer temp
WHERE temp.Customerid = c.Customerid
AND temp.pdate >= MIN(c.pdate)
AND temp.pdate <= c1.endperiod
GROUP BY temp.productid HAVING temp.productid ='Y'
)AS countOfY
FROM Customer c
CROSS APPLY
(
SELECT TOP 1 c1.pdate AS endperiod
FROM Customer c1
WHERE c1.Customerid = c.Customerid
AND c1.pdate >= c.pdate
AND
(
DATEDIFF(MONTH, c.pdate, c1.pdate) < 6
OR
(
SELECT TOP 1 t.pdate FROM Customer t
WHERE t.Customerid = c.Customerid
AND t.pdate < c1.pdate
) IS NULL
)
ORDER BY c1.pdate DESC
)AS c1 GROUP BY c1.endperiod, c.Customerid
;WITH CTE_DateRanges AS (
SELECT
customerid,
productid,
MIN(purchase_date) AS period_start,
DATEADD(MM, 6, MIN(purchase_date)) AS period_end
FROM
My_Table
GROUP BY
customerid,
productid
)
SELECT
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,
COUNT(*)
FROM
CTE_DateRanges DR
INNER JOIN My_Table MT ON
MT.customerid = DR.customerid AND
MT.productid = DR.productid AND
MT.purchase_date BETWEEN DR.period_start AND DR.period_end
GROUP BY
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,

SQL Group By Issue with same item ID

I am trying to track the total number of sales a rep has along with the amount of time he was clocked into work.
I have the following two tables:
table1:
employeeID | item | price | timeID
----------------------------------------
1 | 1 | 12.92 | 123
1 | 2 | 10.00 | 123
1 | 2 | 10.00 | 456
table2:
ID | minutes_in_shift
--------------------------
123 | 45
456 | 15
I would join these two queries with the following SQL:
SELECT
t1.employeeID, t1.item, t1.price, t1.shiftID, t2.minutes_in_shift
FROM table1 t1
JOIN table 2 t2 ON (t2.ID = t1.timeID)
Which would return the following table:
employeeID | item | price | timeID | minutes_in_shift
---------------------------------------------------
1 | 1 | 12.92 | 123 | 45
1 | 2 | 10.00 | 123 | 45
1 | 2 | 10.00 | 456 | 15
I would like for the consolidate results, however, to have this outcome:
employeeID | itemsSold | priceTotals | totaltimeworked
-----------------------------------------------------------------
1 | 3 | 32.92 | 60
I could use COUNT and SUM for the items and price but I cannot figure out how to properly show the total time worked in the manner it appears above.
Note: I am only having trouble with calculating the time worked. In shift 123 - employee 1 was working 45 minutes, regardless of how many items he sold.
Any suggestions?
If you wish to use the sample data as they are you will need to extract the shifts and sum the minutes, like this:
with a as (
select employeeID, count(*) itemsSold, sum(price) priceTotals
from Sampletable1
group by employeeID),
b as (
select employeeID, shiftID, max(minutes_in_shift) minutes_in_shift
from Sampletable1
group by employeeID, shiftID),
c as (
select employeeID, sum(minutes_in_shift) totaltimeworked
from b
group by employeeID)
select a.employeeID, a.itemsSold, a.priceTotals, c.totaltimeworked
from a inner join c on a.employeeID = c.employeeID
However, with your existing tables the select statement will be much easier:
with a as (
select employeeID, timeID, count(*) itemsSold, sum(price) priceTotals
from table1
group by employeeID, timeID)
select a.employeeID, sum(a.itemsSold), sum(a.priceTotals), sum(table2.minutes_in_shift) totaltimeworked
from a inner join table2 on a.timeID = table2.ID
group by a.employeeID
I think this query should do what you want:
SELECT t1.employeeID,
count(t1.item) AS itemsSold,
sum(t1.price) AS priceTotals,
sum(DISTINCT t2.minutes_in_shift) AS totaltimeworked
FROM table1 t1
JOIN table2 t2 ON (t2.ID = t1.timeID)
GROUP BY t1.employeeID;
Check on SQL Fiddle