Need Monthly Report in sql server query - sql

I have 2 tables(Product1, product2).
Product1 Product2
------------------------------ -------------------------------------
PID ProdDesc ProdCode Date PID
------------------------------ -------------------------------------
1 Sony s001 2013/01/21 1
2 Samsung sa01 2013/02/27 1
3 LG L001 2013/03/14 2
4 Toshiba T001 2013/04/18 3
5 Philips P001 2013/05/30 4
2013/06/12 5
I need to generate a query that joins the data from the 2 tables and groups the results by month.
Here is my desired Output:
ProdDesc Jan2013 Feb2013 Mar2013 Apr2013 May2013 Jun2013 Jul2013 Aug2013 Sep2013 Oct2013 Nov2013 Dec2013
--------------------------------------------------------------------------------------------------
Sony 1 1 0 0 0 0 0 0 0
Samsung 0 0 1 0 0 0 0 0 0
LG 0 0 0 1 0 0 0 0 0
Toshiba 0 0 0 0 1 0 0 0 0
Philips 0 0 0 0 0 1 0 0 0

You'll need to use a PIVOT to project your result in columns, although some manipulation is required to prepare the data beforehand, to generate your column names and group data per month:
SELECT ProdDesc, [Jan2013],[Feb2013],[Mar2013],[Apr2013],
[May2013],[Jun2013]
FROM
(
SELECT ProdDesc, YearMonth, COUNT(P2PID) AS ProdCount
FROM
(
SELECT p1.ProdDesc,
CAST(DATENAME(MONTH, [DATE]) AS VARCHAR(3)) +
DATENAME(YEAR, [DATE]) AS YearMonth,
p2.PID as P2PID
FROM Product1 p1
LEFT JOIN Product2 p2
ON p1.PID = p2.PID
) x
GROUP BY ProdDesc, YearMonth
) y
PIVOT
(
SUM(ProdCount)
for [YearMonth] IN ([Jan2013],[Feb2013],[Mar2013],
[Apr2013],[May2013],[Jun2013])
) pv;
SqlFiddle here
It is quite likely that you'll want to determine the columns (MonYYYY) dynamically. Have a look here on how to do this.
You can use ISNULL or COALESCE to replace NULLS in the output, if necessary ISNULL([Jan2013], 0) AS Jan2013

Related

Number of Rows Between Polarity Changes SQL

I want to count the number of rows between polarity changes grouped by id in SQL. I'm thinking that there may be a clever way to use window functions to get the job done but I don't know what it is.
Consider data like this:
id
polarity
date
1
0
12/1
1
1
12/2
1
0
12/3
1
0
12/4
1
1
12/5
2
0
12/1
2
0
12/2
2
0
12/3
2
1
12/4
2
0
12/5
2
0
12/6
2
0
12/7
2
1
12/8
Is there a way to count the number of rows between each change in polarity to get something like this :
id
n
1
1
1
2
2
3
2
3
You can do:
select id, count(*) as n
from (
select *,
sum(i) over(partition by id order by date) as g
from (
select *, case when polarity <> lag(polarity)
over(partition by id order by date)
then 1 else 0 end as i
from t
) x
) y
group by id, g
having max(polarity) = 0

How to add a flag column based on column values and COUNT() of records partitioned by customer in SQL Server

My SQL Server table looks like this:
DayNo. Customer AgentsInvolved CallID DesiredFlag
-------------------------------------------------------
0 AAA 1 1858 0
0 AAA 3 1859 0
2 AAA 1 1860 0
0 BBB 2 1862 0
0 CCC 1 1863 1
0 DDD 3 1864 0
9 DDD 1 1865 0
9 DDD 4 1866 0
How do I add a new column to say;
WHEN (DayNo. = 0, and AgentsInvolved = 1) AND COUNT(callID) = 1 (grouped by customer)
THEN 1
ELSE 0
The column values I understand can be wrapped into a case statement but not sure how to add the condition for the COUNT(CallID). See the DesiredFlag column to see the result I'm trying to achieve.
Any help would be great.
I would use NOT EXISTS :
SELECT t.*,
(CASE WHEN NOT EXISTS (SELECT 1
FROM table t1
WHERE t1.Customer = t.Customer AND t1.CallID <> t.CallID
) AND t.DayNo = 0 AND t.AgentsInvolved = 1
THEN 1
ELSE 0
END) AS DesiredFlag
FROM table t;
with SAMPLE DATA:
declare #T Table (DayNo int,Customer varchar(10),AgentsInvolved int,CallID int)
insert into #T values
(0,'AAA',1,1858),
(0,'AAA',3,1859),
(2,'AAA',1,1860),
(0,'BBB',2,1862),
(0,'CCC',1,1863),
(0,'DDD',3,1864),
(9,'DDD',1,1865),
(9,'DDD',4,1866)
you can get your result (as i understand correctly) by:
;with summation as
(
select Customer, count(1) as qty
from #T
group by Customer
)
select
T.*,
coalesce(F.flag, 0) as flag
from
#T T
inner join summation s on s.Customer = T.Customer
outer apply (select 1 as flag where DayNo = 0 and AgentsInvolved = 1 and qty = 1) F
RESULTS:
DayNo Customer AgentsInvolved CallID flag
----------- ---------- -------------- ----------- -----------
0 AAA 1 1858 0
0 AAA 3 1859 0
2 AAA 1 1860 0
0 BBB 2 1862 0
0 CCC 1 1863 1
0 DDD 3 1864 0
9 DDD 1 1865 0
9 DDD 4 1866 0

Need help writing query that inserts a new row drawing its data from two rows under the same ID

I am writing a data fix SQL script that is to insert a new record using data from two rows that are under the same ID.
My table looks like the following:
AccountID | ActivityId | Debit | Credit | DisplayDetails | TransactionDate
=========================================================================
1 1 100 0 Display Details1 2015-02-02
1 2 0 0 Display Details1 2018-02-02
1 1 300 0 Display Details1 1999-02-02
1 2 0 0 Display Details1 2000-02-02
2 1 200 0 Display Details2 2017-02-02
2 2 0 0 Display Details2 2017-04-06
2 12 0 200 Display Details2 2015-04-06
3 1 200 0 Display Details3 2015-02-04
3 3 0 200 Display Details3 2015-06-02
4 1 100 0 Display Details4 2016-02-02
4 2 0 0 Display Details4 2016-06-02
ActivityId 1 writes to Debit
ActivityId 2 writes to neither, but is needed in my conditions
ActivityId 3 writes to Credit
ActivityId 12 writes to Credit
My data fix is to find ActivityId 1 with an associated ActivityId 2 but NOT an associated 12 or 3 under the same AccountID.
I have written a query that meets these conditions:
SELECT A.AccountID
INTO #temp
FROM Account A WITH(NOLOCK)
WHERE ActivityId IN (1, 2, 3, 12)
GROUP BY A.AccountID
HAVING SUM(CASE WHEN A.ActivityId = 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN A.ActivityId = 2 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN A.ActivityId = 3 THEN 1 ELSE 0 END) = 0 AND
SUM(CASE WHEN A.ActivityId = 12 THEN 1 ELSE 0 END) = 0;
Now that I have the affected data, I need to insert a new record with ActivityId 12 using DisplayDetails from the associated Accounts ActivityId 2 and TransactionDate from ActivityId 1.
I also need to insert to field Credit using the Debit value from ActivityId 1.
The background behind this is each Activity 1 and 2 under the same AccountId SHOULD have an ActivityId 12 too, however some of these were missed, hence the need for this fix script.
Below is my approach:
-- Uses AccountIds retrieved above to then get data from parent table.
SELECT A.AccountID, A.ActivityId, A.DebitAmount, A.CreditAmount, A.DisplayDetails, A.TransactionDate
INTO #temp2
FROM Account A
JOIN #temp T
ON T.AccountID = A.AccountID
-- Get ActivityId 2 data
SELECT *
INTO #ActivityTwo
FROM #temp2 T
WHERE T.ActivityId = 2
INSERT INTO Account (AccountID, ActivityId, DebitAmount, CreditAmount, DisplayDetails, TransactionDate)
SELECT T.AccountID, 12, 0, T.DebitAmount, S.DisplayDetails, T.TransactionDate
FROM #temp2 T WITH(NOLOCK)
JOIN #ActivityTwo S
ON T.AccountID = S.AccountID
WHERE T.ActivityId = 1
However if I have multiple Activity 1 and Activity 2 under the same AccountId, my script inserts duplicate rows.
AccountID | ActivityId | Debit | Credit | DisplayDetails | TransactionDate
=========================================================================
1 1 100 0 Display Details1 2015-02-02
1 2 0 0 Display Details1 2018-02-02
1 1 300 0 Display Details1 1999-02-02
1 2 0 0 Display Details1 2000-02-02
1 12 0 100 Display Details1 2015-02-02
1 12 0 100 Display Details1 2015-02-02
1 12 0 300 Display Details1 1999-02-02
1 12 0 300 Display Details1 1999-02-02
2 1 200 0 Display Details2 2017-02-02
2 2 0 0 Display Details2 2017-04-06
2 12 0 200 Display Details2 2015-04-06
3 1 200 0 Display Details3 2015-02-04
3 3 0 200 Display Details3 2015-06-02
4 1 100 0 Display Details4 2016-02-02
4 2 0 0 Display Details4 2016-06-02
4 12 0 100 Display Details4 2016-02-02
As you can see, AccountID 1 has 4 extra 12 records, when it should only have two 12 records.
AccountID 4 worked correctly though, so I believe the problem lies with an account having multiple ActivityId 1 and 2s.
Why is this happening? What can I do to fix this?
Does anyone have a better approach to this problem?
Any help would be greatly appreciated.
Thanks!
Since you do not have any way to associate Activity 1 rows with Activity 2 rows, I think you need a SELECT DISTINCT on your second insert.
SELECT DISTINCT A.AccountID, A.ActivityId, A.DebitAmount, A.CreditAmount,
A.DisplayDetails, A.TransactionDate
INTO #temp2
FROM Account A
JOIN #temp T
ON T.AccountID = A.AccountID

Multiple times Grouping with where in SQL Server

I have a source table like this
ProductName SaleReceipt SaleCode
--------------------------------
F-Apple 1001 1
F-Orange 1002 2
G-Rice 1003 3
G-Barile 1005 4
G-Oats 1006 1
V-Carrot 1007 4
V-Cabbage 1008 3
V-Potato 1009 1
V-Tomato 1010 1
Chocolate 1011 4
Cookies 1012 1
Cakes 1013 2
I need to create a report like this
30 Day delay 60 Day Delay 90 day delay 120 day delay
Fruits 1 1 0 0
Grains 1 0 1 1
Vegetables 2 0 1 1
Other category 1 1 0 1
The conditions to create the report are:
All ProductName start with F is grouped as fruits
All ProductName start with G is grouped as Grains
All ProductName start with V is grouped as Vegetables
Other product name go in the other category
30 day delay: count of (SaleReceipt) with a SaleCode=1
60 day delay: count of (SaleReceipt) with a SaleCode=2
90 day delay: count of (SaleReceipt) with a SaleCode=3
120 day delay: count of (SaleReceipt) with a SaleCode>=4
I could not find how to do grouping two times. I am using SQL Server 2014.
You can use case in a group by. Or use a lookup table:
select coalesce(c.name, 'Other category'),
sum(case when salescode = 1 then 1 else 0 end) as salecode_1,
sum(case when salescode = 2 then 1 else 0 end) as salecode_2,
sum(case when salescode = 3 then 1 else 0 end) as salecode_3,
sum(case when salescode = 4 then 1 else 0 end) as salecode_4
from t left join
(values ('F-%', 'Fruit'), ('G-%' , 'Grain'), ('V-%', 'Vegetable')
) c(pattern, name)
on t.productname like c.pattern
group by coalesce(c.name, 'Other category');

SQL Query. limit an update per rows if condition is X and Y for the same ID number

Have the following table tblTrans where
Trans_ID Trans Sequence Trans_PointsEarned Trans_PointsApplied
4452 1 1 1
4452 2 1 1
4452 3 0 1
4462 1 1 1
4462 2 1 1
4462 3 1 1
4462 4 1 1
4462 5 1 1
9101 1 0 1
9101 2 0 1
9101 3 0 1
9101 4 0 1
(useless table doesnt work)
I need to set the following on another field per every customer ID.
So Customer_OverallPoints
4452 = 2 (doesn't count 0's)
4462 = 4 (I want to cap the points to 4 based on the sequence and transID and customerID)
9101 = 0 (dont count 0's).
This needs to be applied to thousands of records based on customerID and TransID where Trans_Sequence is within the same Trans_ID and it only counts the first 4 rows that have the Trans_pointsEarned = 1.
I tried putting a psuedocode together but it just looked ridicilous and I can't even come up with the logic for this.
Thanks
Assuming that TransId is really the customer id, I think the basic logic is just an aggregation:
select t.TransId,
(case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end) as Customer_OverallPoints
from tblTrans t
group by t.TransId;
You can put this into an update statement as:
update customers c
set Customer_OverallPoints = (select (case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end)
from tblTrans t
where t.TransId = c.CustomerId
);