SELF JOIN to Update Rows Of the Table - sql

I have a Table with these columns
- ProjectId
- Generation
- Expected
- CarryOver
I am trying to update my already populated table in this fashion:
Generation = Integral Part of ((Generation + CarryOver of Previous Row)/10 )
CarryOver = decimal part of ((Generation + CarryOver of Previous Row)/10 )
where Previous Row and Current Row both have same projectId
Below is the query am using to achieve this:
UPDATE TTable
SET
TTable.Expected=(TTable.Generation+ ISNULL(STable.CarryOver,0)),
TTable.CarryOver =(TTable.Generation+ISNULL(STable.CarryOver,0))-CONVERT(INT,(TTable.Generation+ISNULL(STable.CarryOver,0)))
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
)TTable,
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) STable
Where
TTable.RowNumber = STable.RowNumber+1 AND
TTable.ProjectId = STable.ProjectId
....but something strange happens, update happens only for first two rows. For other rows,
ISNULL(STable.CarryOver,0) returns 0. why??
Please help me. or suggest some other way to achieve this
EDIT: on running the query
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 12 0.752
10 2011-12-01 00:00:00.000 12 2011 10.632 10 0.632
10 2012-01-01 00:00:00.000 01 2012 11.928 11 0.928
10 2012-02-01 00:00:00.000 02 2012 7.580 7 0.580
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 1 0.778
data is generated as shown above. notice how the logic doesn't work after 3row
Original Ouput. Before running the update query:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 NULL NULL
10 2011-12-01 00:00:00.000 12 2011 10.632 NULL NULL
10 2012-01-01 00:00:00.000 01 2012 11.928 NULL NULL
10 2012-02-01 00:00:00.000 02 2012 7.580 NULL NULL
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 NULL NULL

The following query will give you the expected results
(Test code https://gist.github.com/1969171 for those that want to play)
PID Date M Y Generation Expected CarryOver
10 2011-10-01 10 2011 56.748 56 0.748
10 2011-11-01 11 2011 12.004 12 0.752
10 2011-12-01 12 2011 10.632 11 0.384
10 2012-01-01 01 2012 11.928 12 0.312
10 2012-02-01 02 2012 7.58 7 0.892
100 2011-12-01 12 2011 5.897 5 0.897
100 2012-01-01 01 2012 0.881 1 0.778
DECLARE rowItems CURSOR FOR
SELECT ProjectId, [Month], [Year], Generation FROM TTable
ORDER BY ProjectId,[Year] ,CAST([Month] as int)
DECLARE #p int, #m VARCHAR(5), #y int, #g FLOAT, #priorP int,
#carryOver FLOAT, #expected FLOAT
OPEN rowItems
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
SET #priorP = -1
SET #carryOver = 0.0
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT #p = #priorP SET #carryOver = 0.0
SET #expected = #g+#carryOver
SET #carryOver = ROUND(#expected-FLOOR(#expected),3,0)
UPDATE TTable
SET EXPECTED = FLOOR(#expected), CarryOver = #carryOver
WHERE ProjectId = #p and [Month] = #m and [Year] = #y
SET #priorP = #p
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
END
CLOSE rowItems
DEALLOCATE rowItems
SELECT * FROM TTable
You need to do this in a loop. The query you use is getting the original value of the carry over column not the updated value.

Sorry about the confutation. Here is my suggestion.
Test data
CREATE TABLE TTable
(
ProjectId INT,
MonthYear DATETIME,
Month VARCHAR(5),
Year INT,
Generation FLOAT,
Expected FLOAT,
CarryOver FLOAT
)
INSERT INTO TTable
VALUES
(10,'2011-10-01 00:00:00.000','10',2011,56.748,56,0.748),
(10,'2011-11-01 00:00:00.000','11',2011,12.004,NULL,NULL),
(10,'2011-12-01 00:00:00.000','12',2011,10.632,NULL,NULL),
(10,'2012-01-01 00:00:00.000','01',2012,11.928,NULL,NULL),
(10,'2012-02-01 00:00:00.000','02',2012,7.580,NULL,NULL),
(100,'2011-12-01 00:00:00.000','12',2011,5.897,5,0.897),
(100,'2012-01-01 00:00:00.000','01',2012,0.881,NULL,NULL)
Query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
SELECT
CTE.ProjectId,
CTE.MonthYear,
CTE.Month,
CTE.Year,
CTE.Generation,
CTE.Expected,
(
CTE.CarryOver+ISNULL(Previus.CarryOver,0)
) AS CarryOver
FROM
CTE
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Result
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778
Update query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
UPDATE TTable
SET TTable.CarryOver=CTE.CarryOver+ISNULL(Previus.CarryOver,0),
TTable.Expected=CTE.Expected
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY TTable.ProjectId,
TTable.Month) AS RowNbr,
TTable.CarryOver,
TTable.Expected
FROM
TTable
) AS TTable
JOIN CTE
ON TTable.RowNbr=CTE.RowNbr
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Now when selecting for the updated table. Then you will get this result:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778

This is how I did it:
For updating the first record corresponding to each ProjectId:
Update TTable
SET
TTable.Expected=(TTable.Generation/10),
TTable.CarryOver =(TTable.Generation/10)-CONVERT(INT,TTable.Generation/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) TTable where TTable.RowNumber in
(
Select Min(TTable.RowNumber)as RowNumber From
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable GROUP by ProjectId
)
And for updating rest of the records:
DECLARE #RowNumber INT = -99
DECLARE #CarryOver decimal(4,3) =0
DECLARE #Expected INT =0
DECLARE #ProjectId int =0
WHILE #RowNumber IS NOT NULL
BEGIN
SET #RowNumber= (SELECT MIN(RowNumber) FROM (
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable
WHERE TTable.RowNumber> #RowNumber)
IF #RowNumber IS NULL
BREAK
SELECT #CarryOver= TTable.CarryOver,#ProjectId=TTable.ProjectId FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber
UPDATE TTable
SET TTable.Expected =(TTable.Generation+#CarryOver)/10,
CarryOver = (TTable.Generation+#CarryOver)/10 - CONVERT(INT,(TTable.Generation+#CarryOver)/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber+1 AND TTable.ProjectId=#ProjectId
END

Related

Updating duplicate records on the basis of date, so no two dates are same

Hkey | Observation dt| Retriment_dt | Name |Code | Masterkey
---------+------------+------
23 10/8/2018 01/01/3030 Sam XYZ 99
23 10/8/2018 01/01/3030 Sam XYZ 98
23 10/8/2018 01/01/3030 Sam XYZ 97
21 11/8/2018 01/01/3030 JOHN TGI 65
21 11/8/2018 01/01/3030 JOHN TGI 64
21 11/8/2018 01/01/3030 JOHN TGI 63
30 11/8/2018 01/01/3030 Chris MNY 70
Ok, so assume i have this table and my table total count is over a million i want to update the table (Observation dt and retirement dt ) for the duplicate rows - I dont want to update all the observation dates to the same date but i want them to be different by a day. I have manually inputed it below. How can i do it in Sql or SSIS or in any programming language. This is Mssql Db table. I am new to sql and would appreciate any help. Thanks!
Combination of HKey and Observation_dt is the primary key and when i apply the constraint it will throw an error, so i am trying to retire all the duplicate records by changing both retirement_dt and observation_dt. Retirement dt will be todays date and observation_dt can be any date-1 (incrementally for each duplicate date)
What it should look like when the code runs
Hkey | Observation dt| Retriment_dt | Name |Code | Masterkey
---------+------------+------
23 10/8/2018 01/01/3030 Sam XYZ 99
23 10/7/2018 12/17/2018 Sam XYZ 98
23 10/6/2018 12/17/2018 Sam XYZ 97
21 11/8/2018 01/01/3030 JOHN TGI 65
21 11/7/2018 12/17/2018 JOHN TGI 64
21 11/6/2018 12/17/2018 JOHN TGI 63
30 11/8/2018 01/01/3030 Chris MNY 70
You can use the following solution:
IF OBJECT_ID('tempdb..#YourTable') IS NOT NULL
DROP TABLE #YourTable
SELECT
V.Hkey,
[Observation dt] = CONVERT(DATE, V.[Observation dt]),
[Retriment_dt] = CONVERT(DATE, V.[Retriment_dt])
INTO
#YourTable
FROM
(VALUES
(23,'2018-08-10','3030-01-01'),
(23,'2018-08-10','3030-01-01'),
(23,'2018-08-10','3030-01-01'),
(21,'2018-08-10','3030-01-01'),
(21,'2018-08-10','3030-01-01'),
(21,'2018-08-10','3030-01-01'),
(30,'2018-08-10','3030-01-01')) V(Hkey, [Observation dt], [Retriment_dt])
;WITH DuplicateRecords AS
(
SELECT
T.HKey,
T.[Observation dt]
FROM
#YourTable T
GROUP BY
T.HKey,
T.[Observation dt]
HAVING
COUNT(1) > 1
),
RowNumber AS
(
SELECT
T.Hkey,
T.[Observation dt],
T.[Retriment_dt],
RowNumberByHkey = ROW_NUMBER() OVER (PARTITION BY T.Hkey ORDER BY T.[Observation dt], T.[Retriment_dt])
FROM
#YourTable AS T
INNER JOIN DuplicateRecords AS D ON
T.Hkey = D.Hkey AND
T.[Observation dt] = D.[Observation dt]
),
UpdatedValues AS
(
SELECT
R.Hkey,
R.[Observation dt],
R.[Retriment_dt],
NewObservationDT = DATEADD(
DAY,
-1 * (R.RowNumberByHkey - 1),
R.[Observation dt]),
NewRetirementDT = GETDATE(),
R.RowNumberByHkey
FROM
RowNumber AS R
),
RecordsToUpdate AS
(
-- Need a row number to be able to update correctly, since the record is duplicated (need an ID to join)
SELECT
T.Hkey,
T.[Observation dt],
T.[Retriment_dt],
RowNumberByHkey = ROW_NUMBER() OVER (PARTITION BY T.Hkey ORDER BY T.[Observation dt], T.[Retriment_dt])
FROM
#YourTable AS T
)
UPDATE T SET
[Observation dt] = R.NewObservationDT,
[Retriment_dt] = R.NewRetirementDT
FROM
RecordsToUpdate AS T
INNER JOIN UpdatedValues AS R ON
T.HKey = R.HKey AND
T.[Observation dt] = R.[Observation dt] AND
T.RowNumberByHkey = R.RowNumberByHkey
SELECT
*
FROM
#YourTable AS T
ORDER BY
T.Hkey,
T.[Observation dt] DESC
Result:
Hkey Observation dt Retriment_dt
21 2018-08-10 2018-12-18
21 2018-08-09 2018-12-18
21 2018-08-08 2018-12-18
23 2018-08-10 2018-12-18
23 2018-08-09 2018-12-18
23 2018-08-08 2018-12-18
30 2018-08-10 3030-01-01
It was a little tricky because you need to update duplicate records with different values each, so you need to generate some kind of unique ID (I used row number) to be able to match them.
The way to generate different days was to apply a DATEADD with the row number, which was partitioned by HKey. This generates different days with 1 day difference.
My coworker did this in a similar way, But thank you for the replies. I have posted the code used.
SELECT [healthplanentryhistory_avi_hkey]
,[effective_date]
,[expiration_date]
,[healthplanentryhistoryid]
,[hospitalmasterid]
,[plancode]
,[plangeneration]
,[code]
,[pawvalue]
,[quantitycoveredbyplan]
,[healthplanentrymasterid]
,[healthplanentryid]
,[healthplanid]
,[lastupdate]
,[origpawvalue]
,[active_ind]
,[hash_diff]
,[source_sys_id]
,[create_date]
,[update_date]
,cnt
,Rank
INTO ##tmphph
FROM (
SELECT *
,COUNT(*) OVER (PARTITION BY [healthplanentryhistory_avi_hkey]) AS cnt
,RANK() OVER (
PARTITION BY [healthplanentryhistory_avi_hkey] ORDER BY healthplanentryhistoryid DESC
) AS Rank
FROM [atf_healthplanentryhistory_avi]
) AS t
WHERE t.cnt > 1
AND t.rank > 1
ORDER BY healthplanentryhistoryid DESC;
---SELECT * FROM ##tmphph where healthplanentryhistory_avi_hkey = 0x039E7D809F8138B703FC9991E9D8F655
MERGE INTO [atf_healthplanentryhistory_avi] atf
USING ##tmphph TEMP
ON atf.healthplanentryhistory_avi_hkey = TEMP.[healthplanentryhistory_avi_hkey]
AND atf.effective_date = TEMP.effective_date
AND atf.healthplanentryhistoryid = TEMP.healthplanentryhistoryid
AND TEMP.rank > 1
WHEN MATCHED
THEN
UPDATE
SET atf.effective_date = getdate() - TEMP.rank /*This will update the effective_date to efective_date - rank#*/
,expiration_date = getdate() - TEMP.rank
,active_ind = 0;
DROP TABLE ##tmphph
Using Temp table:
Create Table #tbl
(
hkey Int,
Observation Date,
Retriment Date
)
Insert Into #tbl Values
(23,'2018-10-08','3030-01-01'),
(23,'2018-10-08','3030-01-01'),
(23,'2018-10-08','3030-01-01'),
(21,'2018-11-08','3030-01-01'),
(21,'2018-11-08','3030-01-01'),
(21,'2018-11-08','3030-01-01'),
(30,'2018-11-08','3030-01-01')
Select Row_Number() OVER(Order By (Select Null)) As raworder,* Into #temp From #tbl
Select hkey,
DateAdd(Day,-Row_Number() Over (Partition By hkey Order By hkey)+1 , Observation) As newDT,
Case When (Row_Number() Over (Partition By hkey Order By hkey) = 1) Then Retriment Else Convert(Date,GetDate()) End As Retriment
From #temp
Order By raworder
Result:
hkey newDT Retriment
23 2018-10-08 3030-01-01
23 2018-10-07 2018-12-18
23 2018-10-06 2018-12-18
21 2018-11-08 3030-01-01
21 2018-11-07 2018-12-18
21 2018-11-06 2018-12-18
30 2018-11-08 3030-01-01

How to keep the leap year when substracting 1 year

I have this query that gives me a given date for each of the past 15 years. When my starting date is February 29 it does not return the 29 for year 2012, 2008 and 2004. How can I have this query to return the 29 for those years?
DECLARE #TempDate1 TABLE (Entry_Date Date)
INSERT INTO #TempDate1 values ('2016-02-29')
;WITH
a AS(SELECT DATEADD(yy,-1,Entry_Date) d, DATEADD(yy,-1,Entry_Date) d2,0 i
FROM #TempDate1
UNION all
SELECT DATEADD(yy,-1,d),DATEADD(yy,-1,d2),i+1 FROM a WHERE i<14),
b AS(SELECT d,d2, DATEDIFF(dd,0,d)%7 dd,i FROM a)
SELECT
d AS Entry_Date
FROM b
It returns this:
Entry_Date
2015-02-28
2014-02-28
2013-02-28
2012-02-28
2011-02-28
2010-02-28
2009-02-28
2008-02-28
2007-02-28
2006-02-28
2005-02-28
2004-02-28
2003-02-28
2002-02-28
2001-02-28
While I would like to have this:
Entry_Date
2015-02-28
2014-02-28
2013-02-28
2012-02-29
2011-02-28
2010-02-28
2009-02-28
2008-02-29
2007-02-28
2006-02-28
2005-02-28
2004-02-29
2003-02-28
2002-02-28
2001-02-28
Perhaps DateAdd in concert with an ad-hoc tally table
Example
Declare #YourTable Table ([Entry_Date] date)
Insert Into #YourTable Values
('2016-02-29')
,('2015-07-22')
Select YearNr = N
,Anniv = dateadd(YEAR,N*-1,Entry_Date)
From #YourTable A
Cross Apply (
Select Top 15 N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1
) B
Returns
Simply by using EOMONTH function (SQL Server 2012 and above):
DECLARE #TempDate1 TABLE (Entry_Date Date)
INSERT INTO #TempDate1 values ('2016-02-29')
;WITH
a AS(SELECT DATEADD(yy,-1,Entry_Date) d, DATEADD(yy,-1,Entry_Date) d2,0 i
FROM #TempDate1
UNION all
SELECT DATEADD(yy,-1,d),DATEADD(yy,-1,d2),i+1 FROM a WHERE i<14),
b AS(SELECT d,d2, DATEDIFF(dd,0,d)%7 dd,i FROM a)
SELECT EOMONTH(d) AS Entry_Date
FROM b;
Rextester Demo
Rewrite tour query like this... Not only will handle leap years without jumping through hoops, it's orders of magnitude more efficient than what you currently have.
DECLARE #BaseDate DATE = '2016-02-29';
SELECT
Entry_Date = DATEADD(YEAR, t.n, #BaseDate)
FROM
(VALUES (-1),(-2),(-3),(-4),(-5),
(-6),(-7),(-8),(-9),(-10),
(-11),(-12),(-13),(-14),(-15) ) t (n);
Results...
Entry_Date
----------
2015-02-28
2014-02-28
2013-02-28
2012-02-29
2011-02-28
2010-02-28
2009-02-28
2008-02-29
2007-02-28
2006-02-28
2005-02-28
2004-02-29
2003-02-28
2002-02-28
2001-02-28
EDIT: Same functionality when used with a table of dates (I stole John's table)
DECLARE #YourTable TABLE (id INT, Entry_Date DATE);
INSERT INTO #YourTable VALUES (1, '2016-02-29'), (2, '2015-07-22');
SELECT
yt.id,
Entry_Date = DATEADD(YEAR, t.n, yt.Entry_Date)
FROM
#YourTable yt
CROSS APPLY (VALUES (-1),(-2),(-3),(-4),(-5),
(-6),(-7),(-8),(-9),(-10),
(-11),(-12),(-13),(-14),(-15) ) t (n);
GO
Results...
id Entry_Date
----------- ----------
1 2015-02-28
1 2014-02-28
1 2013-02-28
1 2012-02-29
1 2011-02-28
1 2010-02-28
1 2009-02-28
1 2008-02-29
1 2007-02-28
1 2006-02-28
1 2005-02-28
1 2004-02-29
1 2003-02-28
1 2002-02-28
1 2001-02-28
2 2014-07-22
2 2013-07-22
2 2012-07-22
2 2011-07-22
2 2010-07-22
2 2009-07-22
2 2008-07-22
2 2007-07-22
2 2006-07-22
2 2005-07-22
2 2004-07-22
2 2003-07-22
2 2002-07-22
2 2001-07-22
2 2000-07-22

Get Invoice count of sales by stores for the last three months

This is my CustomerDetails table.
CustomerID CustCodeID
25 1
65 8
35 2
112 8
45 2
975 8
364 1
48 8
69 1
97 8
33 1
11 8
93 2
10 8
21 1
65 8
74 2
53 8
This is my Fact_SalesMetrics table.
Date Sales # CustomerID
2015-03-23 00:00:00.000 42895 25
2015-03-13 00:00:00.000 53920 53
2015-03-23 00:00:00.000 44895 65
2015-03-13 00:00:00.000 43920 35
2015-03-23 00:00:00.000 48895 112
2015-03-13 00:00:00.000 47920 45
2015-03-23 00:00:00.000 46895 975
2015-03-13 00:00:00.000 45920 48
2015-03-23 00:00:00.000 40895 69
2015-03-13 00:00:00.000 40920 11
2015-03-23 00:00:00.000 41895 33
2015-03-13 00:00:00.000 49920 21
......
I wish to make output like below:
CustCodeID March 2015
1 4
2 2
8 7
Which means the customer who has codeID '1' has 4 orders on March, 2 has 2 orders and like that.
To make this happen, I queried like below and got it working:
select CustCodeID,sum(March) as 'March 2015' from (
select bb.CustCodeID, aa.March from (
(SELECT count(distinct([Sales #])) as 'March', customerid
FROM [SalesData].[dbo].[Fact_SalesMetrics] a
where date >= '2015-03-01 00:00:00.000' and date <= '2015-03-31 00:00:00.000'
and customerid in (select customerid from CustomerDetails)
group by customerid ) as aa inner join (select customerid,CustCodeID from CustomerDetails ) as bb on aa.customerid=bb.customerid
)
) as dd group by CustCodeID
Now I wish to calculate the invoices count for the last three months like below:
CustCodeID March 2015 February 2015 January 2015
1 4 ? ?
2 2 ? ?
8 7 ? ?
Can anyone help me to achieve this?
Since you have column of type DateTime, you need to convert it to Month Year format. Use DateName in Sql Server to extract the monthname and year. You should then find the count of CustomerID and GROUP BY with the new date format and CustCodeID. You should use this query as the source query for the table to pivot.
If the values of months are known in advance, you can use Static Pivot by hard-coding the column names
SELECT CustCodeID,[March 2015],[February 2015],[January 2015]
FROM
(
SELECT CD.CustCodeID,COUNT(CD.CustomerID) COUNTOfCustomerID,
DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE]) MONTHS
FROM CustomerDetails CD
JOIN Fact_SalesMetrics FS ON CD.CustomerID=FS.CustomerID
GROUP BY CD.CustCodeID,DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE])
)TAB
PIVOT
(
MIN(COUNTOfCustomerID)
FOR MONTHS IN([March 2015],[February 2015],[January 2015])
)P
SQL FIDDLE
If the numbers of columns are not known in advance, you can go for Dynamic Pivot.
For that the first step is to get the column names to be displayed after pivot from the rows. In the following query, it will select the columns for the last 3 months.
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(MONTHS)
FROM
(
SELECT TOP 3 DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE]) MONTHS
from Fact_SalesMetrics
GROUP BY '01 ' + DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE]),
DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE])
ORDER BY CAST('01 ' + DATENAME(MONTH,[DATE])+' ' + DATENAME(YEAR,[DATE]) AS DATE) DESC
) c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Now execute Pivot query using Dynamic sql
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT CD.CustCodeID,COUNT(CD.CustomerID) COUNTOfCustomerID,
DATENAME(MONTH,[DATE])+'' '' + DATENAME(YEAR,[DATE]) MONTHS
FROM CustomerDetails CD
JOIN Fact_SalesMetrics FS ON CD.CustomerID=FS.CustomerID
GROUP BY CD.CustCodeID,DATENAME(MONTH,[DATE])+'' '' + DATENAME(YEAR,[DATE])
) x
PIVOT
(
-- Specify the values to hold in pivoted column
MIN([COUNTOfCustomerID])
-- Get the column names from variable
FOR [MONTHS] IN('+#cols+')
) p
ORDER BY CustCodeID;'
EXEC SP_EXECUTESQL #query
SQL FIDDLE

Get the latest value on each date

I have two tables with the following structure:
DECLARE #Table1 TABLE
(
IdColumn INT,
DateColumn DATETIME
)
DECLARE #Table2 TABLE
(
IdColumn INT,
DateColumn DATETIME,
Value NUMERIC(18,2)
)
What i want to do is get the latest value from table2 having a less or equal date in table1.
This is the query i build:
SET NOCOUNT ON
DECLARE #Table1 TABLE
(
IdColumn INT,
DateColumn DATETIME
)
DECLARE #Table2 TABLE
(
IdColumn INT,
DateColumn DATETIME,
Value NUMERIC(18,2)
)
DECLARE #RefDate DATETIME='2012-09-01'
DECLARE #NMonths INT
DECLARE #MonthsCounter INT=1
SELECT #NMonths=DATEDIFF(MM,'2012-09-01','2013-03-01')
WHILE #MonthsCounter<=#NMonths
BEGIN
INSERT INTO #Table1
SELECT 1,#RefDate
SET #RefDate=DATEADD(MM,1,#RefDate);
SET #MonthsCounter+=1;
END
INSERT #Table2
SELECT 1,'2012-09-01',1000
UNION
SELECT 1,'2012-12-01',5000
UNION
SELECT 1,'2013-01-01',3000
SELECT
T1.IdColumn,
T1.DateColumn,
T2.Value
FROM #Table1 T1
LEFT JOIN #Table2 T2
ON T2.IdColumn=T1.IdColumn AND T1.DateColumn>=t2.DateColumn
The problem is when a new value comes with a more recent date, i get all values until that date.
IdColumn DateColumn Value
----------- ----------------------- ---------------------------------------
1 2012-09-01 00:00:00.000 1000.00
1 2012-10-01 00:00:00.000 1000.00
1 2012-11-01 00:00:00.000 1000.00
1 2012-12-01 00:00:00.000 1000.00
1 2012-12-01 00:00:00.000 5000.00
1 2013-01-01 00:00:00.000 1000.00
1 2013-01-01 00:00:00.000 5000.00
1 2013-01-01 00:00:00.000 3000.00
1 2013-02-01 00:00:00.000 1000.00
1 2013-02-01 00:00:00.000 5000.00
1 2013-02-01 00:00:00.000 3000.00
The desired output is this one:
IdColumn DateColumn Value
----------- ----------------------- ---------------------------------------
1 2012-09-01 00:00:00.000 1000.00
1 2012-10-01 00:00:00.000 1000.00
1 2012-11-01 00:00:00.000 1000.00
1 2012-12-01 00:00:00.000 5000.00
1 2013-01-01 00:00:00.000 3000.00
1 2013-02-01 00:00:00.000 3000.00
How can i solve this ?
Thanks.
I am just posting Gordon's Answer with correct syntax :
select t1.*,
(select top 1 value
from #table2 t2
where t2.IdColumn = t1.IdColumn and
t2.DateColumn <= t1.DateColumn
order by t2.DateColumn desc
) t2value
from #table1 t1
I would do this with a correlated subquery:
select t1.*,
(select top 1 value
from #table2 t2
where t2.idColumn = t1.idColumn and
t2.dateColumn <= t1.dateColumn
order by t2.dateColumn desc
) t2value
from #table1 t1;
After comment, try this:
INSERT INTO #Table1 (IdColumn, DateColumn)
SELECT IdColumn, DateColumn
FROM #Table2 t
WHERE NOT EXISTS
(
SELECT 'X'
FROM #Table2 tcopy
where t.IdColumn = tcopy.IdColumn
and convert(date, t.DateColumn) = convert(date, tcopy.DateColumn)
and tCopy.DateColumn > t.DateColumn
)
I used ">" because you told me, there are no rows with the same date/time in your table2

SQL Generate Missing Records in SELECT

Using MS SQL Server 8.0.760 (2000)
I have a Table like this:
Table A
Day | Hour | Value
2012-10-01| 12 | 780
2012-10-01| 14 | 678
2012-11-02| 08 | 123
2012-11-02| 09 | 473
Expected Result should be:
Day | Hour | Value
2012-10-01| 00 | 0
2012-10-01| 01 | 0
2012-10-01| 02 | 0
2012-10-01| .. | ..
2012-10-01| 12 | 780
2012-10-01| 13 | 0
2012-10-01| 14 | 678
2012-10-01| .. | ..
2012-10-01| 22 | 0
2012-10-01| 23 | 0
2012-10-01| 24 | 0
2012-11-02| 00 | 0
2012-11-02| 01 | 0
2012-11-02| 02 | 0
2012-11-02| .. | ..
2012-11-02| 08 | 123
2012-11-02| 09 | 473
2012-11-02| .. | ..
2012-11-02| 22 | 0
2012-11-02| 23 | 0
2012-11-02| 24 | 0
So the missing hours are generated with Zero Values..
Any Idea?
EDIT 1
Tried this:
DECLARE #tmpHours TABLE
(
tmpHour varchar(2)
)
INSERT INTO #tmpHours VALUES ('00')
INSERT INTO #tmpHours VALUES ('01')
...
INSERT INTO #tmpHours VALUES ('23')
INSERT INTO #tmpHours VALUES ('24')
SELECT * FROM [A]
FULL JOIN #tmpHours tmp ON tmp.[tmpHour] = [A].[Hour]
ORDER BY [Day], [Hour], [tmpHour]
But this generates this:
Day Hour Value tmpHour
NULL NULL NULL 00
NULL NULL NULL 01
NULL NULL NULL 02
NULL NULL NULL 03
NULL NULL NULL 04
NULL NULL NULL 05
NULL NULL NULL 06
NULL NULL NULL 07
NULL NULL NULL 10
NULL NULL NULL 11
NULL NULL NULL 13
NULL NULL NULL 15
NULL NULL NULL 16
NULL NULL NULL 17
NULL NULL NULL 18
NULL NULL NULL 19
NULL NULL NULL 20
NULL NULL NULL 21
NULL NULL NULL 22
NULL NULL NULL 23
NULL NULL NULL 24
2012-10-01 00:00:00.000 12 780 12
2012-10-01 00:00:00.000 14 678 14
2012-11-02 00:00:00.000 08 123 08
2012-11-02 00:00:00.000 09 473 09
You can create an table (possibly temporary) called container your hours (your example shows 25 hours from 00 to 24 but, I guess you want 24 hours). Then you can do an outer join between Table A with your HOURS table. This will generate NULL values instead of 0 values. If need be, you can use a function to convert NULL values to 0.
EDIT refactored answer into a single SQL query:
SELECT X.*, A.VALUE
FROM A
RIGHT OUTER JOIN
(
SELECT *
FROM
(SELECT DISTINCT A.DAY FROM A) DAYS,
(SELECT 0 HOUR
UNION ALL SELECT 1 HOUR
UNION ALL SELECT 2 HOUR
UNION ALL SELECT 3 HOUR
UNION ALL SELECT 4 HOUR
UNION ALL SELECT 5 HOUR
UNION ALL SELECT 6 HOUR
UNION ALL SELECT 7 HOUR
UNION ALL SELECT 8 HOUR
UNION ALL SELECT 9 HOUR
UNION ALL SELECT 10 HOUR
UNION ALL SELECT 11 HOUR
UNION ALL SELECT 12 HOUR
UNION ALL SELECT 13 HOUR
UNION ALL SELECT 14 HOUR
UNION ALL SELECT 15 HOUR
UNION ALL SELECT 16 HOUR
UNION ALL SELECT 17 HOUR
UNION ALL SELECT 18 HOUR
UNION ALL SELECT 19 HOUR
UNION ALL SELECT 20 HOUR
UNION ALL SELECT 21 HOUR
UNION ALL SELECT 22 HOUR
UNION ALL SELECT 23 HOUR
) HOURS
) X
ON X.DAY = A.DAY AND X.HOUR = A.HOUR
Try with following Steps. It will be very dynamic one. (Note : Same date 00 and 24 won't come)
--Create Table :
Create Table #Table
(
Day Date,
Hour Int,
Value Int
)
Go
-- Insert Values :
Insert into #Table Values('2012-10-01','12','780')
Insert into #Table Values('2012-10-01','14','678')
Insert into #Table Values('2012-10-02','08','123')
Insert into #Table Values('2012-10-02','09','473')
Go
--View Data :
Select * from #Table
Declare #TempTable as Table
(Day Date,Hour Int,Value Int)
Declare #Date Date
Declare #i Int
Set #i = 0
--Using Cursor :
Declare cur Cursor
for
Select Distinct Day from #Table
Open cur
Fetch Next From cur Into #Date
While ##Fetch_status = 0
BEGIN
While (#i <=23)
Begin
If not exists (Select 1 from #Table Where Day =#Date and Hour = #i )
Begin
Insert into #TempTable Values (#Date,#i,0)
End
Else
Begin
Insert into #TempTable
Select Day,Hour,Value from #Table Where Day =#Date and Hour = #i
End
Set #i = #i + 1
End
Fetch Next From cur Into #Date
End
Close cur
Deallocate cur
--Results :
Select * from #TempTable
--Clean Up :
Drop Table #Table