Hard time creating PIVOT in SQL Server - sql-server

Hi all I am very much confused about how to implement PIVOT in this scenario
AccId Year Month AccType Value
225 2012 7 1 2
225 2012 7 2 0
225 2012 7 3 0
226 2012 7 1 3
226 2012 7 2 0
226 2012 7 3 0
The main problem I am not able to solve is AccId is joined with AccountTable and AccType is also joined with AccountType table I need output like this:
AccId AccName Year Month AccType AccTypeName Value AccType AccTypeName Value AccType AccTypeName Value
225 ABC 2012 7 1 AAA 2 2 BBB 0 3 CCC 0
226 ABC 2012 7 1 AAA 3 2 BBB 0 3 CCC 0
Any Help please. Thanks.

DECLARE #Table1 TABLE
(AccId int, Year int, Month int, AccType int, Value int)
;
INSERT INTO #Table1
(AccId, Year, Month, AccType, Value)
VALUES
(225, 2012, 7, 1, 2),
(225, 2012, 7, 2, 0),
(225, 2012, 7, 3, 0),
(226, 2012, 7, 1, 3),
(226, 2012, 7, 2, 0),
(226, 2012, 7, 3, 0)
;
DECLARE #Tabletype TABLE
(AccId int, Type Varchar(6))
INSERT INTO #Tabletype
(AccId,Type )values (225,'AAA'), (226,'BBB')
;
;WITH CTE AS (
Select AccId, Year, Month,1 AS Acct1,2 AS Acct2,3 AS Acct3,Type from (
select T.AccId, T.Year, T.Month,T.AccType,TT.Type,ROW_NUMBER()OVER(PARTITION BY T.AccId ORDER BY T.Year,T.month)RN from #Table1 T
INNER JOIN #Tabletype TT
ON T.AccId = TT.AccId)T
PIVOT(MAX(RN) FOR AccType IN([1],[2],[3]))PVT
GROUP BY AccId, Year, Month,pvt.Type)
, CTE2 AS (
Select AccId, Year, Month,[0] AS val1,[2] AS val2,[3] AS val3,Type from (
select T.AccId, T.Year, T.Month, T.Value,TT.Type,ROW_NUMBER()OVER(PARTITION BY T.AccId ORDER BY T.Year,T.month)RN from #Table1 T
INNER JOIN #Tabletype TT
ON T.AccId = TT.AccId)T
PIVOT(MAX(RN) FOR value IN([0],[2],[3]))PVTt
GROUP BY AccId, Year, Month,PVTt.[0],PVTt.[2],PVTt.[3],PVTt.Type)
select c.AccId,
c.Year,
C.Month,
c.Acct1,
c.Type,
ISNULL(cc.val1,0)val1,
C.Acct2,
c.Type,
ISNULL(CC.val2,0)val2,
C.Acct3,
c.Type,
ISNULL(CC.val3,0)val3 from CTE c
inner join CTE2 cc
on c.AccId = cc.AccId

Related

How to insert missing years in temporary table in MS SQL Server

I work with Sales and problem is that this table does not have records for each client for every year. Records are missing randomly. Instead i need to have those years there and put 0 for sales for those years for my analysis.
I have limited knowledge of SQL. Can anybody help on this one? What i have as of now and what i would like to have is shown below.
I have thoughts to use LAG() function, but missing records can be for 2 years in a row or 3. I am not sure how to tackle such problem.
What I have now:
Client_ID
SalesYear
Sales
1
2010
12
1
2012
20
1
2013
21
1
2016
14
What i need to have:
Client_ID
SalesYear
Sales
1
2010
12
1
2011
0
1
2012
20
1
2013
21
1
2014
0
1
2015
0
1
2016
14
You need a complete list of years to outer-join with.
You can do this a number of ways, the basic principle would be:
with y as (
select * from (values(2010),(2011),(2012),(2013),(2014),(2015),(2016))y(y)
)
insert into t (Client_Id, SalesYear, Sales)
select 1, y.y, 0
from y
where not exists (select * from t where t.SalesYear = y.y);
Something like this might help:
DECLARE #Sales TABLE
(Client_ID int, SalesYear int, Sales money)
INSERT INTO #Sales(Client_ID, SalesYear, Sales) SELECT 1, 2010, 12
INSERT INTO #Sales(Client_ID, SalesYear, Sales) SELECT 1, 2012, 20
INSERT INTO #Sales(Client_ID, SalesYear, Sales) SELECT 1, 2013, 21
INSERT INTO #Sales(Client_ID, SalesYear, Sales) SELECT 1, 2016, 14;
with years as
(
select 2000 as theYear
UNION ALL
select y.theYear + 1 as theYear
from years y
where y.theYear + 1 <= YEAR(GetDate())
)
select
Y.theYear, S.Client_ID, S.Sales
FROM
Years Y
LEFT JOIN
#Sales S ON S.SalesYear = Y.theYear
option (maxrecursion 0)
You can change "2000" to something more appropriate.

To remove duplication of data if within 7 days

Following is my table and sample data
DECLARE #Employee_Log table(ID int,eid int, ecode varchar(100), emp_startdate date)
INSERT INTO #Employee_Log
SELECT 1, 1, 'aaa','2019-01-01'
UNION ALL
SELECT 2, 1, 'aaa','2019-01-05'
UNION ALL
SELECT 3, 1, 'bbb','2019-01-03'
UNION ALL
SELECT 4, 2, 'aaa','2019-01-03'
UNION ALL
SELECT 5, 1, 'aaa','2019-02-01'
UNION ALL
SELECT 6, 1, 'aaa','2019-02-15'
UNION ALL
SELECT 7, 1, 'aaa','2019-02-19'
UNION ALL
SELECT 8, 1, 'aaa','2019-02-28'
In the above data I want to remove the duplication based on eid and ecode .If the emp_startdate are within 7 days then take the latest data and ignore the rest data.
I tried the following code but how to add the condition check for week range
SELECT
ROW_NUMBER() OVER(PARTITION BY eid,ecode ORDER BY emp_startdate desc) as rownum,
ID,eid,ecode,emp_startdate
FROM #Employee_Log
I want the result as shown below
ID eid ecode emp_startdate
2 1 aaa 2019-01-05
5 1 aaa 2019-02-01
4 2 aaa 2019-01-03
7 1 aaa 2019-02-19
8 1 aaa 2019-02-28
3 1 bbb 2019-01-03
I am still not sure what you want to happen if more than 2 events happen in the same 7 days. But this solution will get the latest date of all series of dates where the difference between dates is 7 days or less.
select ID,eid,ecode,emp_startdate
from
(
select ID,
eid,
ecode,
emp_startdate,
datediff(day
,emp_startdate
,lead(emp_startdate)
over
(partition by eid,ecode order by emp_startdate)) l
from #Employee_Log
) a
where l is null or l>7
ID eid ecode emp_startdate
-- --- ----- -------------
3 1 bbb 2019-01-03
2 1 aaa 2019-01-05
5 1 aaa 2019-02-01
7 1 aaa 2019-02-19
8 1 aaa 2019-02-28
4 2 aaa 2019-01-03
The following query will give you what you have asked to get in plain English in your question but your sample data and desired output contradicts your own question:
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY eid , ecode , YEAR(emp_startdate)
, DATEPART(WEEK, emp_startdate)
ORDER BY emp_startdate DESC
) AS rownum
, ID
, eid
, ecode
, emp_startdate
FROM #Employee_Log
) x
WHERE x.rownum = 1;

T-SQL - 3 month moving sum - preceding null values

Using SQL Server 2016. I have the following data table (sample)
Target Date Total
-----------------
2018-01-24 1
2018-02-28 1
2018-03-02 1
2018-03-08 1
2018-03-15 1
2018-03-30 1
2018-04-16 1
2018-04-18 1
2018-04-30 1
I would like to get to get a 3 month moving sum (grouping is by month):
Target Date Total_Sum
-----------------------
2018-01-01 1
2018-02-01 2
2018-03-01 6
2018-04-01 8
Ok, this should get the answer you want. Firstly you need to total the value your months, then you can do a running total for the last 3 months:
CREATE TABLE SampleTable (TargetDate date, Total int);
GO
INSERT INTO SampleTable
VALUES ('20180124', 1),
('20180228', 1),
('20180302', 1),
('20180308', 1),
('20180315', 1),
('20180330', 1),
('20180416', 1),
('20180418', 1),
('20180430', 1);
GO
SELECT *
FROM SampleTable;
GO
WITH Months AS (
SELECT DATEADD(MONTH,DATEDIFF(MONTH, 0, TargetDate),0) AS TargetMonth, SUM(Total) AS MonthTotal
FROM SampleTable
GROUP BY DATEADD(MONTH,DATEDIFF(MONTH, 0, TargetDate),0))
SELECT TargetMonth,
SUM(MonthTotal) OVER (ORDER BY TargetMonth ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS Last3Months
FROM Months;
GO
DROP TABLE SampleTable;
GO
Pls try the below code
;WITH CTE(TargetDate,Total)
AS
(
SELECT '2018-01-24', 1 UNION ALL
SELECT '2018-02-28', 1 UNION ALL
SELECT '2018-03-02', 1 UNION ALL
SELECT '2018-03-08', 1 UNION ALL
SELECT '2018-03-15', 1 UNION ALL
SELECT '2018-03-30', 1 UNION ALL
SELECT '2018-04-16', 1 UNION ALL
SELECT '2018-04-18', 1 UNION ALL
SELECT '2018-04-30', 1
)
SELECT STUFF(TargetDate,9,2,'01') AS TargetDate
,Total_Sum
FROM
(
SELECT TargetDate,Total_Sum
,ROW_NUMBER()OVER(PARTITION BY Total_Sum ORDER BY TargetDate) AS Seq
FROM
(
SELECT TargetDate
,SUM(Total )OVER(ORDER BY MONTH(TargetDate) ) AS Total_Sum
FROM CTE
)dt
)fnl
WHERE Seq=1
Result
TargetDate Total_Sum
---------------------
2018-01-01 1
2018-02-01 2
2018-03-01 6
2018-04-01 9

Finding count of records in given interval

I have a table with user logins, and I want to find the user who logged in to the site more than 3 times in a 5 day period.
For example my table is:
id | user_id | login_date
---+---------+--------------
1 | 10 | 10.1.2014 00:00
2 | 10 | 11.1.2014 10:10
3 | 12 | 11.1.2014 11:00
4 | 10 | 11.1.2014 12:00
5 | 12 | 12.1.2014 00:00
6 | 10 | 13.1.2014 10:00
7 | 12 | 18.1.2014 00:00
8 | 12 | 22.1.2014 09:00
For this example table, I want to choose user_id 10 because he/she logged in more than 3 times in a 5 day period.
Could you please help me for that?
edit: i forgot to mention that database is sql server 2008
You could do it by self joining the table on user_id where the JOIN takes records that are within 5 days of the record it is joining to like so:
CREATE TABLE #login
(
id INT ,
user_id INT ,
login_date DATETIME
)
INSERT INTO #login
( id, user_id, login_date )
VALUES ( 1, 10, '2014-01-10 00:00' ),
( 2, 10, '2014-01-11 10:10' ),
( 3, 12, '2014-01-11 11:00' ),
( 4, 10, '2014-01-11 12:00' ),
( 5, 12, '2014-01-12 00:00' ),
( 6, 10, '2014-01-13 10:00' ),
( 7, 12, '2014-01-18 00:00' ),
( 8, 12, '2014-01-22 09:00' )
SELECT t1.user_id ,
t1.login_date AS FirstDateInLoginPeriod ,
COUNT(t2.user_id) AS LoginCount
FROM #login t1
INNER JOIN #login t2 ON t2.user_id = t1.user_id
AND t2.login_date
BETWEEN t1.login_date AND DATEADD(DAY, 5, t1.login_date)
GROUP BY t1.user_id ,
t1.login_date
HAVING COUNT(t2.user_id) > 3
DROP TABLE #login
Produces:
user_id FirstDateInLoginPeriod LoginCount
----------------------------------------------
10 2014-01-10 00:00:00.000 4
If you are using SQL Server 2012+ then you can use LEAD window function to calculate difference in days between any 3 consecutive records:
select distinct USER_ID
from (
select USER_ID,
datediff(d, login_date,
LEAD(login_date, 2) OVER (PARTITION BY user_id
ORDER BY login_date)) as diffDates
from users ) t
where t.diffDates <= 5
Then simply select those USER_IDs for which the difference in days is equal to or less than 5.
SQL Fiddle Demo
If you are using SQL Server 2008 or 2005, then you can use ROW_NUMBER in conjuction with a self join in order to simulate LEAD function:
;WITH CTE AS (
SELECT id, user_id, login_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn
FROM users
)
SELECT DISTINCT user_id
FROM (
SELECT c1.user_id,
DATEDIFF(d, c1.login_date, c2.login_date) AS diffInDays
FROM CTE AS c1
INNER JOIN CTE AS c2 ON c1.user_id = c2.user_id AND c1.rn = c2.rn - 2
) t
WHERE t.diffInDays <= 5
diffInDays in the above query is essentially the rolling difference in days between any 3 consecutive user logins.
SQL Fiddle Demo
You can use following query to get your result.
As you can see you can use DATEADD function for calculating reference date and use having to filter logins count to be more or equal 3
Select count(*),user_id
from table_name
where login_date>=DATEADD (day,-5,GETDATE())
group by user_id
having COUNT(*) >=3

Need to get count based condition in select clause based on group by Date in tsql

DECLARE #testTable TABLE (OrderDate date, StoreType int, Fruits int, Vegetables int, eggs int)
INSERT INTO #testTable VALUES(getDate(), 1, 4, 6, 9);
INSERT INTO #testTable VALUES(getDate(), 1, 5, 7, 3);
INSERT INTO #testTable VALUES(getDate(), 2, 8, 2, 1);
INSERT INTO #testTable VALUES(getDate()-1, 1, 7, 2, 8);
INSERT INTO #testTable VALUES(getDate()-1, 1, 0, 9, 4);
INSERT INTO #testTable VALUES(getDate()-1, 2, 5, 6, 1);
INSERT INTO #testTable VALUES(getDate()-2, 1, 7, 2, 5);
INSERT INTO #testTable VALUES(getDate()-2, 2, 6, 6, 6);
INSERT INTO #testTable VALUES(getDate()-2, 1, 5, 6, 3);
INSERT INTO #testTable VALUES(getDate()-3, 1, 9, 3, 3);
INSERT INTO #testTable VALUES(getDate()-3, 2, 6, 0, 1);
INSERT INTO #testTable VALUES(getDate()-3, 1, 2, 7, 4);
-- NEED TO MODIFY QUERY
SELECT
YEAR(OrderDate) AS [YEAR]
,MONTH(OrderDate) AS [MONTH]
,DAY(OrderDate) AS [DAY]
,CASE WHEN StoreType IN (1,2) THEN SUM(Fruits) END AS FruitCount
,CASE WHEN StoreType IN (1,2) THEN SUM(Vegitables) END AS VegetablesCount
,CASE WHEN StoreType IN (1,2) THEN SUM(eggs) END AS EggsCount
,StoreType
,(CASE WHEN StoreType = 1 THEN SUM(Fruits + vegitables + eggs) END) AS [CountBY Stype(1)]
FROM
#testTable
GROUP BY
YEAR(OrderDate),
MONTH(OrderDate),
DAY(OrderDate),
StoreType
Current result set:
YEAR MONTH DAY FruitCount VegitalbesCount EggsCount StoreType CountBySType(1)
2015 2 9 11 10 7 1 28
2015 2 9 6 0 1 2 NULL
2015 2 10 12 8 8 1 28
2015 2 10 6 6 6 2 NULL
2015 2 11 7 11 12 1 30
2015 2 11 5 6 1 2 NULL
2015 2 12 9 13 12 1 34
2015 2 12 8 2 1 2 NULL
Expected / desired output:
YEAR MONTH DAY FruitCount VegitalbesCount EggsCount CountBY Stype(1)
2015 2 9 17 10 8 28
2015 2 10 18 14 14 28
2015 2 11 12 17 13 30
2015 2 12 17 15 13 34
Is there any way can I achieve result set like above(EXPECTED)? We can use sub query, but due to high volume of data query makes so slow.
You need to take out the StoreType from GroupBy
Try this:
SELECT
YEAR(OrderDate) AS [YEAR]
,MONTH(OrderDate) AS [MONTH]
,DAY(OrderDate) AS [DAY]
,SUM(CASE WHEN StoreType IN (1,2) THEN Fruits END )AS FruitCount
,SUM(CASE WHEN StoreType IN (1,2) THEN Vegetables END )AS VegetablesCount
,SUM(CASE WHEN StoreType IN (1,2) THEN eggs END) AS EggsCount
,SUM(CASE WHEN StoreType = 1 THEN Fruits + Vegetables + eggs END)
FROM #testTable
GROUP BY
YEAR(OrderDate),
MONTH(OrderDate),
DAY(OrderDate)
I change query below to achieve. thanks for everyone for reading and your solutions.
SELECT YEAR(OrderDate) AS [YEAR]
,MONTH(OrderDate) AS [MONTH]
,DAY(OrderDate) AS [DAY]
,SUM(COALESCE(Fruits,0)) AS FruitCount
,SUM(COALESCE(Vegitables,0)) AS VegitalbesCount
,SUM(COALESCE(eggs,0)) AS EggsCount,
SUM(CASE WHEN StoreType IN (1) THEN COALESCE(Fruits,0)+ COALESCE(Vegitables,0)+ COALESCE(eggs,0) END) [Total]
FROM #testTable
GROUP BY YEAR(OrderDate),MONTH(OrderDate),DAY(OrderDate)

Resources