sql query to create columns with arithmetic expressions - sql-server

I have my sql tables and query as shown below :
CREATE TABLE #ABC([Year] INT, [Month] INT, Stores INT);
CREATE TABLE #DEF([Year] INT, [Month] INT, SalesStores INT);
CREATE TABLE #GHI([Year] INT, [Month] INT, Products INT);
INSERT #ABC VALUES (2013,1,1);
INSERT #ABC VALUES (2013,1,2);
INSERT #ABC VALUES (2013,2,3);
INSERT #DEF VALUES (2013,1,4);
INSERT #DEF VALUES (2013,1,5);
INSERT #DEF VALUES (2013,2,6);
INSERT #GHI VALUES (2013,1,7);
INSERT #GHI VALUES (2013,1,8);
INSERT #GHI VALUES (2013,2,9);
INSERT #GHI VALUES (2013,3,10);
My current query is
SELECT T.[Year],
T.[Month]
-- select the sum for each year/month combination using a correlated subquery (each result from the main query causes another data retrieval operation to be run)
,
(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Stores],
(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_SalesStores],
(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Products]
FROM (
-- this selects a list of all possible dates.
SELECT [Year],
[Month]
FROM #ABC
UNION
SELECT [Year],
[Month]
FROM #DEF
UNION
SELECT [Year],
[Month]
FROM #GHI) AS T;
Which returns
+------+-------+------------+-----------------+--------------+
| Year | Month | Sum_Stores | Sum_SalesStores | Sum_Products |
+------+-------+------------+-----------------+--------------+
| 2013 | 1 | 3 | 9 | 15 |
| 2013 | 2 | 3 | 6 | 9 |
| 2013 | 3 | NULL | NULL | 10 |
+------+-------+------------+-----------------+--------------+
What I want to do is to add two more columns to my query which shows
Sum_SalesStores/Sum_Products & Sum_SalesStores/Sum_Stores per each month and then sort the query based on the two expressions. Can anyone tell me how its possible ?

One way would just be to chuck your entire existing query into a CTE then you can select from that and perform the calculations.
;WITH CTE
AS (
/*Paste your existing query*/
)
SELECT *,
Sum_SalesStores / Sum_Products AS Foo,
Sum_SalesStores / Sum_Stores AS Bar
FROM CTE
ORDER BY Foo,
Bar

I re-factored your code a bit to place the core of the logic in the FROM statement.
SELECT Dates.[Year]
,Dates.[Month]
,SumStores
,SumSalesStores
,SumProducts
,SumSalesStores/SumProducts
,SumSalesStores/SumStores
FROM
(
-- This selects a list of all possible dates.
SELECT [Year],[Month] FROM ABC
UNION SELECT [Year],[Month] FROM DEF
UNION SELECT [Year],[Month] FROM GHI
) AS Dates
Left Join
(
SELECT [Year]
,[Month]
,SumStores = Sum(Stores)
FROM ABC
GROUP BY [Year]
,[Month]
) As Stores
On Dates.[Year] = Stores.[Year]
And Dates.[Month] = Stores.[Month]
Left Join
(
SELECT [Year]
,[Month]
,SumSalesStores = Sum(SalesStores)
FROM DEF
GROUP BY [Year]
,[Month]
) As SalesStores
On Dates.[Year] = SalesStores.[Year]
And Dates.[Month] = SalesStores.[Month]
Left Join
(
SELECT [Year]
,[Month]
,SumProducts = Sum(Products)
FROM GHI
GROUP BY [Year]
,[Month]
) As Products
On Dates.[Year] = Products.[Year]
And Dates.[Month] = Products.[Month]
ORDER BY 1, 2
Here's the SQL Fiddle.

Related

Select ID for corresponding max date using GROUP BY

My table structure as below
Category Sex Last Modified Date Id
7 2 2015-01-16 87603
7 1 2014-11-27 87729
7 2 2018-09-06 87135
7 1 2017-12-27 87568
My sql query as below
SELECT
MAX(Id) AS Id
FROM
Table
GROUP BY
Category, Sex
Result as below
87603
87729
But I would like to get Id as Max Last Modified Date. Correct result should be as below
87135
87568
You can use ROW_NUMBER() to find most recent row per group:
SELECT Id, LastModifiedDate
FROM (
SELECT Id, LastModifiedDate, ROW_NUMBER() OVER (PARTITION BY Category, Sex ORDER BY LastModifiedDate DESC) AS rnk
FROM t
) AS cte
WHERE rnk = 1
Use RANK() if you're interested in finding all rows with ties for LastModifiedDate.
You can also get it as
SELECT T.*
FROM
(
SELECT Sex,
MAX([Last Modified Date]) [Last Modified Date],
Category
FROM T
GROUP BY Sex,
Category
) TT INNER JOIN T ON T.[Last Modified Date] = TT.[Last Modified Date]
WHERE T.Sex = TT.Sex
AND
T.Category = TT.Category;
Returns:
+----------+-----+---------------------+-------+
| Category | Sex | Last Modified Date | Id |
+----------+-----+---------------------+-------+
| 7 | 2 | 06/09/2018 00:00:00 | 87135 |
| 7 | 1 | 27/12/2017 00:00:00 | 87568 |
+----------+-----+---------------------+-------+
We can get the solution by joining the same table with its grouped set:
SELECT MIN(T.Id)
FROM Table T
INNER JOIN (SELECT Category,
Sex,
MAX(LastModifiedDate) AS LastModifiedDate
FROM Table
GROUP BY Category, Sex) GT
ON GT.Category = T.Category
AND GT.Sex = T.Sex
AND GT.LastModifiedDate = T.LastModifiedDate
GROUP BY T.Category, T.Sex
Other option is to use correlated subquery :
select t.*
from table t
where t.LastModifiedDate = (select max(t1.LastModifiedDate)
from table t1
where t1.Category = t.Category and t1.Sex = t.Sex
);
Here are a few different approaches... (in no particular order)
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
GO
CREATE TABLE #TestData (
Category TINYINT NOT NULL,
Sex TINYINT NOT NULL,
LastModifiedDate DATE NOT NULL,
Id INT NOT NULL
);
GO
INSERT #TestData(Category, Sex, LastModifiedDate, Id) VALUES
(7, 2, '2015-01-16', 87603),
(7, 1, '2014-11-27', 87729),
(7, 2, '2018-09-06', 87135),
(7, 1, '2017-12-27', 87568);
GO
/* nonclustered index to support the query. */
CREATE UNIQUE NONCLUSTERED INDEX ix_TestData_Category_Sex_LastModifiedDate
ON #TestData (Category ASC, Sex ASC, LastModifiedDate DESC)
INCLUDE (Id);
GO
--====================================================
-- option 1: TOP(n) WITH TIES...
SELECT TOP (1) WITH TIES
td.Id
FROM
#TestData td
ORDER BY
ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC);
GO
-----------------------------------------------------
-- option 2: Filter on ROW_NUMBER()...
WITH
cte_AddRN AS (
SELECT
td.Id,
rn = ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC)
FROM
#TestData td
)
SELECT
arn.Id
FROM
cte_AddRN arn
WHERE
arn.rn = 1;
GO
-----------------------------------------------------
-- option 3: binary concatination...
SELECT
Id = CONVERT(INT, SUBSTRING(MAX(bv.bin_val), 4, 4))
FROM
#TestData td
CROSS APPLY ( VALUES (CONVERT(BINARY(3), td.LastModifiedDate) + CONVERT(BINARY(4), td.Id)) ) bv (bin_val)
GROUP BY
td.Category,
td.Sex;
GO
--====================================================

Output in special format

May any one please help me how to code the below scenerio in sql.
DECLARE #T TABLE
(
Year VARCHAR (50),
Month VARCHAR (50),
GROUPS VARCHAR (50),
SALESPERNO VARCHAR (50),
Net VARCHAR (50)
)
INSERT #T
SELECT '2014','1','A','6607','109.34' UNION ALL
SELECT '2014','2','A','6607','13.42' UNION ALL
SELECT '2014','3','A', '6607','359.41' UNION ALL
SELECT '2014','1','A', '6608','99.52' UNION ALL
SELECT '2014','2','A','6608','95.62' UNION ALL
SELECT '2014','3','A', '6608','89.63' UNION ALL
SELECT '2014','1','B','8888','340.95' UNION ALL
SELECT '2014','2','B','8888','652.25' UNION ALL
SELECT '2014','3','B','8888','352.26'
SELECT * FROM #T
Here: month 1 = Jan, 2=feb, 3=mar
Output is sum of net for a particular salesperno and groups for jan feb mar and total like this:
January| February | March| QFY | SALES_GROUP |SALESPERSON_NUMBER
109.34 | 13.42 | 359.41| 482.17| A | 6607
99.52 | 95.62 | 89.63 | 284.77| A | 6608
340.95 | 652.25 | 352.26| 1345.46| B | 8888
You can do this in multiple ways. As Tab Alleman suggested one way is with the help of CASE and GROUP BY.
select
SUM(CASE WHEN [Month]=1 THEN [NET] END) [January],
SUM(CASE WHEN [Month]=2 THEN [NET] END) [February],
SUM(CASE WHEN [Month]=3 THEN [NET] END) [March],
SUM([NET]) [QFY],
[GROUPS] [SALES_GROUP],
[SALESPERNO] [SALESPERSON_NUMBER]
from #T
group by
[GROUPS],
[SALESPERNO]
This is another way, with PIVOT
SELECT
[1] [January],
[2] [February],
[3] [March],
[1]+[2]+[3] [QFY],
[GROUPS] [SALES_GROUP],
[SALESPERNO] [SALESPERSON_NUMBER]
FROM #T
PIVOT (
SUM(NET)
FOR [Month] in ([1],[2],[3])
) pvt

Retrieve most frequent values from each column within groups

For each group, grouped using field GRP, I would like to retrieve the most frequently occurring value in column A and the most frequently occurring value in column B, and potentially do this for many other columns.
Sample Data:
GRP | A | B
-----------
Cat | 1 | 1
Cat | 2 | 1
Cat | 3 | 2
Cat | 3 | 3
Dog | 5 | 6
Dog | 5 | 7
Dog | 6 | 7
Expected Output:
GRP | A | B
-----------
Cat | 3 | 1
Dog | 5 | 7
This query achieves that result:
SELECT
freq1.GRP,
freq1.A,
freq2.B
FROM (
SELECT
GRP,
A,
ROW_NUMBER() OVER(PARTITION BY GRP ORDER BY COUNT(*) DESC) AS F_RANK
FROM MyTable
GROUP BY GRP, A
) AS freq1
INNER JOIN (
SELECT
GRP,
B,
ROW_NUMBER() OVER(PARTITION BY GRP ORDER BY COUNT(*) DESC) AS F_RANK
FROM MyTable
GROUP BY GRP, B
) AS freq2 ON freq2.GRP = freq1.GRP
WHERE freq1.F_RANK = 1 AND freq2.F_RANK = 1
It just doesn't look very efficient, and even less so if I were to add a column C, D, etc...
Is there a better way?
I wouldn't say this approach is "better" because it will generate the exact same execution plan. However, I find this type of approach a lot more maintainable as the number of columns might grow. For me this is a lot easier to read.
with GroupA as
(
select Grp
, A
, ROW_NUMBER() over(partition by grp order by count(*) desc) as RowNum
from MyTable
group by Grp, A
)
, GroupB as
(
select Grp
, B
, ROW_NUMBER() over(partition by grp order by count(*) desc) as RowNum
from MyTable
group by Grp, B
)
select a.Grp
, a.A
, b.B
from GroupA a
inner join GroupB b on a.Grp = b.Grp and b.RowNum = 1
where a.RowNum = 1;
An alternative using results ranked in a temp table:
SELECT GRP, A, B,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) ARank,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) BRank
INTO #TMP
FROM MyTable
SELECT t1.GRP,
(SELECT TOP 1 A FROM #TMP WHERE GRP = t1.Grp ORDER BY ARank DESC) A,
(SELECT TOP 1 B FROM #TMP WHERE GRP = t1.Grp ORDER BY BRank DESC) B
FROM MyTable t1
GROUP BY T1.GRP
DROP TABLE #TMP
Full Solution on SQL Fiddle
Schema Setup:
CREATE TABLE MyTable
([GRP] varchar(3), [A] int, [B] int)
;
INSERT INTO MyTable
([GRP], [A], [B])
VALUES
('Cat', 1, 1),
('Cat', 2, 1),
('Cat', 3, 2),
('Cat', 3, 3),
('Dog', 5, 6),
('Dog', 5, 7),
('Dog', 6, 7)
;
Query 1:
SELECT GRP, A, B,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) ARank,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) BRank
INTO #TMP
FROM MyTable
SELECT t1.GRP,
(SELECT TOP 1 A FROM #TMP WHERE GRP = t1.Grp ORDER BY ARank DESC) A,
(SELECT TOP 1 B FROM #TMP WHERE GRP = t1.Grp ORDER BY BRank DESC) B
FROM MyTable t1
GROUP BY T1.GRP
DROP TABLE #TMP
Results:
| GRP | A | B |
|-----|---|---|
| Cat | 3 | 1 |
| Dog | 5 | 7 |
I'll start out this answer by saying this is NOT going to be more efficient to run - it should just be easier to add/subtract columns. To do this you just add them into the code in two places.
You can use dynamic SQL to build your result set like this:
CREATE TABLE ##fields (id INT IDENTITY(1,1),fieldname VARCHAR(255))
INSERT INTO ##fields
( fieldname )
VALUES ('A'),('B') --Add field names here
DECLARE #maxid INT
SELECT #maxid = MAX(id) FROM ##fields
CREATE TABLE ##Output (GRP VARCHAR(3), A INT, B INT) --Add field names here
INSERT INTO ##Output
( GRP )
SELECT DISTINCT GRP FROM MyTable
DECLARE #SQL NVARCHAR(MAX)
DECLARE #i INT = 1
WHILE #i <=#maxid
BEGIN
SELECT #SQL = 'with cte as (SELECT GRP , ' + fieldname + ' ,
ROW_NUMBER() OVER ( PARTITION BY GRP ORDER BY COUNT(*) DESC ) AS F_RANK
FROM MyTable
GROUP BY GRP , ' + fieldname + ')
UPDATE O
SET O.' + fieldname + ' = cte.' + fieldname + '
FROM ##Output O
INNER JOIN cte ON O.GRP = cte.GRP AND cte.F_Rank = 1' FROM ##fields WHERE id = #i
EXEC sp_executesql #sql
SET #i = #i + 1
END
SELECT *
FROM ##Output
DROP TABLE ##fields
DROP TABLE ##Output
Using your simple example above, I received the following performance stats:
Dynamic SQL
CPU = 31
Reads = 504
Duration = 39
Your SQL
CPU = 0
Reads = 6
Duration = 1
Clearly, this way is not a more efficient way of doing this. I did want to throw it out there anyway as an alternative to your current method.
First we create the test data:
DECLARE #MyTable TABLE
(
GRP varchar(10),
A int,
B int
)
INSERT INTO #MyTable
( GRP, A, B)
VALUES
('Cat', 1, 1),
('Cat', 2, 1),
('Cat', 3, 2),
('Cat', 3, 3),
('Dog', 5, 6),
('Dog', 5, 7),
('Dog', 6, 7);
Now we use first_value from a subselect (or a cte if you wanted) and grab the top cat and dog columns
SELECT DISTINCT
GRP,
FIRST_VALUE(A) OVER(PARTITION BY GRP ORDER BY d.A_CNT DESC) AS A_RANK,
FIRST_VALUE(B) OVER(PARTITION BY GRP ORDER BY d.B_CNT DESC) AS B_RANK
FROM
(
SELECT
GRP,
A,
ROW_NUMBER() OVER (PARTITION BY A ORDER BY GRP, A) AS A_CNT,
B,
ROW_NUMBER() OVER (PARTITION BY B ORDER BY GRP, B) AS B_CNT
FROM #MyTable
) d
Output:
GRP A_RANK B_RANK
Cat 3 1
Dog 5 7

Consecutively calculate value between rows in a table

I'm trying to conduct a t-sql which is able to perform some calculation by taking the datetime value of the consecutive row subtract with the datetime value of its previous one.
For example:
Col1 Col2
-------------------------------------------------------------------
row 1: | ENTRY_DOOR_CLOSE | 2/12/2014 16:41:40:4140
row 2: | EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN | 3/12/2014 16:41:40:4140
row 3: | ENTRY_DOOR_CLOSE | 4/12/2014 16:41:40:4140
row 4: | EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN | 5/12/2014 16:41:40:4140
--------------------------------------------------------------------
Result:
Col1 Col2
---------------------------------------------------------------------
Row 1: | Diff | Row2.DateTime - Row1.DateTime
Row 2: | Diff | Row4.DateTime - Row3.DateTime
---------------------------------------------------------------------
Can anyone suggest an idea to resolve this?
In SQL Server 2012+, you can use the lead() function:
select 'Diff' as col1,
datediff(second, col2, col2_next) as diff_in_seconds
from (select t.*, lead(col2) over (order by col2) as col2_next
from table t
) t
where col1 = 'ENTRY_DOOR_CLOSE';
This assumes that the values are interleaved, as in the question.
Just figured out using CTE can solve my issue in case i'm not using SQL 2k12
;WITH valuedTable AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY ScxxID, SxxID ORDER BY RecordTime) AS RowID
, ScxxID
, SxxID
, Exxx
, RecordTime
, ProcessName
FROM
database..xxx
WHERE
ProcessName = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN'
OR
ProcessName = 'ENTRY_DOOR_CLOSE'
)
SELECT
valuedTable.ProcessName
, valuedTable.RecordTime
, nex.ProcessName
, nex.RecordTime
, DATEDIFF(S, valuedTable.RecordTime, nex.RecordTime) DIFF
FROM
valuedTable
INNER JOIN
( valuedTable nex ON nex.RowID = valuedTable.RowID + 1 )
AND
( nex.ProcessName = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' )
if you use sql server 2012 - use this one (your table is ordered, but this one is variabile too for non ordered table):
;WITH CTE AS (SELECT ROW_NUMBER() OVER (ORDER BY Col2) AS RN, Col1, Col2
FROM YourTable)
SELECT 'Diff' AS Col1, DATEDIFF(HOUR,a.Col2,x.Col2) AS Col2
FROM CTE a
CROSS APPLY (SELECT TOP 1 Col2 FROM CTE b WHERE Col1 = 'EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' AND b.RN > a.RN ORDER BY Col2 ASC) x
WHERE Col1 = 'ENTRY_DOOR_CLOSE'
Hope this will help
--CREATE A TEMPORARY TABLE TO HOLD THE GIVEN DATA
DECLARE #Table AS TABLE
(
ID INT IDENTITY(1,1)
,Col1 VARCHAR(50)
,Col2 DATETIMEOFFSET(0)
)
INSERT INTO #Table (COl1,Col2) VALUES ('ENTRY_DOOR_CLOSE', '2014-12-02'),
('EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' , '2014-12-03')
,('ENTRY_DOOR_CLOSE','2014-12-04')
,('EXIT_DOOR_CLOSE_ENTRY_DOOR_OPEN' , '2014-12-05')
--Using common table expression do the following
;WITH CTE AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS RowID
,CONVERT(date,Col2) AS DateColumn
FROM #Table
)
SELECT
'DIF' AS Col1
,DATEDIFF(DD,SEcondCTE.DateColumn,FirstCTE.DateColumn)
FROM
CTE FirstCTE
INNER JOIN
CTE SEcondCTE
ON
FirstCTE.RowID = SEcondCTE.RowID + 1
WHERE FirstCTE.RowID % 2 =0

sql query to find the difference in values from previous month

I have my sql tables and query as shown below :
CREATE TABLE #ABC([Year] INT, [Month] INT, Stores INT);
CREATE TABLE #DEF([Year] INT, [Month] INT, SalesStores INT);
CREATE TABLE #GHI([Year] INT, [Month] INT, Products INT);
INSERT #ABC VALUES (2013,1,1);
INSERT #ABC VALUES (2013,1,2);
INSERT #ABC VALUES (2013,2,3);
INSERT #DEF VALUES (2013,1,4);
INSERT #DEF VALUES (2013,1,5);
INSERT #DEF VALUES (2013,2,6);
INSERT #GHI VALUES (2013,1,7);
INSERT #GHI VALUES (2013,1,8);
INSERT #GHI VALUES (2013,2,9);
INSERT #GHI VALUES (2013,3,10);
My current query is
I have #Year and #Month as parameters , both integers , example #Year = '2013' , #Month = '11'
SELECT T.[Year],
T.[Month]
-- select the sum for each year/month combination using a correlated subquery (each result from the main query causes another data retrieval operation to be run)
,
(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Stores],
(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_SalesStores],
(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Products]
FROM (
-- this selects a list of all possible dates.
SELECT [Year],
[Month]
FROM #ABC where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #DEF where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #GHI where [Year] = #Year and [Month] = #Month) AS T;
Which returns
+------+-------+------------+-----------------+--------------+
| Year | Month | Sum_Stores | Sum_SalesStores | Sum_Products |
+------+-------+------------+-----------------+--------------+
| 2013 | | | | |
| 2013 | | | | |
| 2013 | | | | |
+------+-------+------------+-----------------+--------------+
What I want to do is to add more columns to the query which show the difference from the last month. as shown below.
Example : The Diff beside the Sum_Stores shows the difference in the Sum_Stores from last month to this month.
Something like this :
+------+-------+------------+-----------------+-----|-----|---+-----------------
| Year | Month | Sum_Stores |Diff | Sum_SalesStores |Diff | Sum_Products |Diff|
+------+-------+------------+-----|------------+----|---- |----+--------------|
| 2013 | | | | | | | |
| 2013 | | | | | | | |
| 2013 | | | | | | | |
+------+-------+------------+-----|------------+--- |-----|----+---------| ----
Can anyone tell me how I can modify this to achive my goal.
You ca use a CTE and then self join to get the desired result.
Fiddle Here
;WITH DATA AS (
SELECT T.[Year],
T.[Month]
-- select the sum for each year/month combination using a correlated subquery (each result from the main query causes another data retrieval operation to be run)
,
(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Stores],
(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_SalesStores],
(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Products]
FROM (
-- this selects a list of all possible dates.
SELECT [Year],
[Month]
FROM #ABC where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #DEF where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #GHI where [Year] = #Year and [Month] = #Month
) AS T )
SELECT d1.year,d1.month ,
d1.Sum_Stores , ( isnull(d2.Sum_Stores,0) -d1.Sum_Stores ) AS storeDiff ,
d1.Sum_SalesStores ,( isnull(d2.Sum_SalesStores,0) -d1.Sum_SalesStores ) AS salesStoresDiff,
d1.Sum_Products , ( isnull(d2.Sum_Products,0) -d1.Sum_Products ) AS prodDiff
-- self joining on month -1 to get previous month data
FROM DATA AS d1 LEFT OUTER JOIN DATA AS d2 ON d2.month = d1.month -1
The above query is for illustrative purposes only.The query provided works correctly for the sample data as it contains the data for a year only. You should apply appropriate logic in the on clause of the left outer join to retrieve data which has input data of more than one year.
Try This:
I created a temp table #XYZ with your data and used that to subtract the previous months data. I have to create 2 more variable to take care of spanning years.
If you have any questions, let me know
DECLARE #Year varchar(4)
SET #Year = '2013'
DECLARE #Month varchar(2) set #Month = '1'
DECLARE #DiffYear varchar(4)
Set #DiffYear = DATEPART(yyyy, DATEADD(m,-1,(#Month+'/1/'+#Year) ))
DECLARE #DiffMonth varchar(2)
set #DiffMonth= DATEPART(mm, DATEADD(m,-1,(#Month+'/1/'+#Year) ))
SELECT T.[Year],
T.[Month]
-- select the sum for each year/month combination using a correlated subquery (each result from the main query causes another data retrieval operation to be run)
,
(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Stores],
(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_SalesStores],
(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Products]
INTO #XYZ
FROM (
-- this selects a list of all possible dates.
SELECT [Year],
[Month]
FROM #ABC --where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #DEF --where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #GHI --where [Year] = #Year and [Month] = #Month
)
AS T;
SELECT T.[Year],
T.[Month]
-- select the sum for each year/month combination using a correlated subquery (each result from the main query causes another data retrieval operation to be run)
,
(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Stores],
ISNULL((SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]),0) - ISNULL((SELECT SUM([Sum_Stores])
FROM #XYZ
WHERE [Year] = #DiffYear
AND [Month] = #DiffMonth),(SELECT SUM(Stores)
FROM #ABC
WHERE [Year] = T.[Year]
AND [Month] = T.[Month])) AS [DIFF_Sum_Stores],
(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_SalesStores],
ISNULL((SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]),0) -
ISNULL((SELECT SUM([Sum_SalesStores])
FROM #XYZ
WHERE [Year] = #DiffYear
AND [Month] = #DiffMonth),(SELECT SUM(SalesStores)
FROM #DEF
WHERE [Year] = T.[Year]
AND [Month] = T.[Month])) AS [DIFF_Sum_SalesStores],
(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]) AS [Sum_Products],
ISNULL((SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month]),0) -
ISNULL((SELECT SUM([Sum_Products])
FROM #XYZ
WHERE [Year] = #DiffYear
AND [Month] = #DiffMonth),(SELECT SUM(Products)
FROM #GHI
WHERE [Year] = T.[Year]
AND [Month] = T.[Month])) AS [Diff_Sum_Products]
FROM (
-- this selects a list of all possible dates.
SELECT [Year],
[Month]
FROM #ABC where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #DEF where [Year] = #Year and [Month] = #Month
UNION
SELECT [Year],
[Month]
FROM #GHI where [Year] = #Year and [Month] = #Month
)
AS T;

Resources