How to combine data from different tables using a query - sql-server

I have a set of tables that look like what I have shown below.
Could you let me know how I can achive the desired output ?
CREATE TABLE #ABC([Year] INT, [Month] INT,Customer Varchar(10), SalesofProductA INT);
CREATE TABLE #DEF([Year] INT, [Month] INT,Customer Varchar(10), SalesofProductB INT);
CREATE TABLE #GHI([Year] INT, [Month] INT,Customer Varchar(10), SalesofProductC INT);
INSERT #ABC VALUES (2013,1,'PPP',1);
INSERT #ABC VALUES (2013,1,'QQQ',2);
INSERT #ABC VALUES (2013,2,'PPP',3);
INSERT #DEF VALUES (2013,1,'QQQ',4);
INSERT #DEF VALUES (2013,1,'RRR',5);
INSERT #DEF VALUES (2013,2,'PPP',6);
INSERT #GHI VALUES (2013,1,'QQQ',7);
INSERT #GHI VALUES (2013,2,'RRR',8);
INSERT #GHI VALUES (2013,3,'PPP',9);
INSERT #GHI VALUES (2013,3,'QQQ',10);
I have a query currently that looks like this . #Month and #Year are supplied as parameters
SELECT
-- 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(SalesofProductA) FROM #ABC WHERE [Year]=T.[Year] AND [Month]=T.[Month]) AS [Sum_SalesofProductA]
,(SELECT SUM(SalesofProductB) FROM #DEF WHERE [Year]=T.[Year] AND [Month]=T.[Month]) AS [Sum_SalesofProductB]
,(SELECT SUM(SalesofProductC) FROM #GHI WHERE [Year]=T.[Year] AND [Month]=T.[Month]) AS [Sum_SalesofProductC]
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;
Right now I see an output like this : for a particular value of #Month and #Year
SalesofProductA, SalesofProductB, SalesofProductC
What I would like to see is :
[Customer],SalesofProductA, SalesofProductB, SalesofProductC
Does anyone know how it can be done ?

SELECT Customer
, SUM(COALESCE(P.[ProductA], 0)) AS [ProductA]
, SUM(COALESCE(P.[ProductB], 0)) AS [ProductB]
, SUM(COALESCE(P.[ProductC], 0)) AS [ProductC]
FROM
(
SELECT Customer , SUM(SalesofProductA) AS Sales, 'ProductA' AS ProductName
FROM #ABC
GROUP BY Customer
UNION ALL
SELECT Customer , SUM(SalesofProductB) AS Sales, 'ProductB' AS ProductName
FROM #DEF
GROUP BY Customer
UNION ALL
SELECT Customer , SUM(SalesofProductC) AS Sales, 'ProductC' AS ProductName
FROM #GHI
GROUP BY Customer
) Q
PIVOT ( SUM(Sales)
FOR ProductName
IN ([ProductA], [ProductB], [ProductC])
)P
GROUP BY Customer
Result
Customer ProductA ProductB ProductC
PPP 4 6 9
QQQ 2 4 17
RRR 0 5 8

Related

Translating SQL Server Cursor to Azure Synapse

I have the following code that loops through a table with unique model numbers and creates a new table that contains, for each model numbers, a row based on the year and week number. How can I translate this so it doesn't use a cursor?
DECLARE #current_model varchar(50);
--declare a cursor that iterates through model numbers in ItemInformation table
DECLARE model_cursor CURSOR FOR
SELECT model from ItemInformation
--start the cursor
OPEN model_cursor
--get the next (first value)
FETCH NEXT FROM model_cursor INTO #current_model;
DECLARE #year_counter SMALLINT;
DECLARE #week_counter TINYINT;
WHILE (##FETCH_STATUS = 0) --fetch status returns the status of the last cursor, if 0 then there is a next value (FETCH statement was successful)
BEGIN
SET #year_counter = 2019;
WHILE (#year_counter <= Datepart(year, Getdate() - 1) + 2)
BEGIN
SET #week_counter = 1;
WHILE (#week_counter <= 52)
BEGIN
INSERT INTO dbo.ModelCalendar(
model,
sales_year,
sales_week
)
VALUES(
#current_model,
#year_counter,
#week_counter
)
SET #week_counter = #week_counter + 1
END
SET #year_counter = #year_counter + 1
END
FETCH NEXT FROM model_cursor INTO #current_model
END;
CLOSE model_cursor;
DEALLOCATE model_cursor;
If ItemInformation contains the following table:
model,invoice
a,4.99
b,9.99
c,1.99
d,8.99
then the expected output is:
model,sales_year,sales_week
A,2019,1
A,2019,2
A,2019,3
...
A,2019,52
A,2020,1
A,2020,2
A,2020,3
...
A,2020,51
A,2020,52
A,2020,53 (this is 53 because 2020 is leap year and has 53 weeks)
A,2021,1
A,2021,2
...
A,2022,1
A,2022,2
...
A,2022,52
B,2019,1
B,2019,2
...
D, 2022,52
Using CTE's you can get all combinations of weeks and years within the range required. Then join your data table on.
declare #Test table (model varchar(1), invoice varchar(4));
insert into #Test (model, invoice)
values
('a', '4.99'),
('b', '9.99'),
('c', '1.99'),
('d', '8.99');
with Week_CTE as (
select 1 as WeekNo
union all
select 1 + WeekNo
from Week_CTE
where WeekNo < 53
), Year_CTE as (
select 2019 YearNo
union all
select 1 + YearNo
from Year_CTE
where YearNo <= datepart(year, current_timestamp)
)
select T.model, yr.YearNo, wk.WeekNo
from Week_CTE wk
cross join (
select YearNo
-- Find the last week of the year (52 or 53) -- might need to change the start day of the week for this to be correct
, datepart(week, dateadd(day, -1, dateadd(year, 1, '01 Jan ' + convert(varchar(4),YearNo)))) LastWeek
from Year_CTE yr
) yr
cross join (
-- Assuming only a single row per model is required, and the invoice column can be ignored
select model
from #Test
group by model
) T
where wk.WeekNo <= yr.LastWeek
order by yr.YearNo, wk.WeekNo;
As you have advised that using a recursive CTE is not an option, you can try using a CTE without recursion:
with T(N) as (
select X.N
from (values (0),(0),(0),(0),(0),(0),(0),(0)) X(N)
), W(N) as (
select top (53) row_number() over (order by ##version) as N
from T T1
cross join T T2
), Y(N) as (
-- Upper limit on number of years
select top (12) 2018 + row_number() over (order by ##version) AS N
from T T1
cross join T T2
)
select W.N as WeekNo, Y.N YearNo, T.model
from W
cross join (
select N
-- Find the last week of the year (52 or 53) -- might need to change the start day of the week for this to be correct
, datepart(week, dateadd(day, -1, dateadd(year, 1, '01 Jan ' + convert(varchar(4),N)))) LastWeek
from Y
) Y
cross join (
-- Assuming only a single row per model is required, and the invoice column can be ignored
select model
from #Test
group by model
) T
-- Filter to required number of years.
where Y.N <= datepart(year, current_timestamp) + 1
and W.N <= Y.LastWeek
order by Y.N, W.N, T.model;
Note: If you setup your sample data in future with the DDL/DML as shown here you will greatly assist people attempting to answer.
I don't like to see a loop solution where a set solution can be found. So here goes Take II with no CTE, no values and no row_number() (the table variable is just to simulate your data so not part of the actual solution):
declare #Test table (model varchar(1), invoice varchar(4));
insert into #Test (model, invoice)
values
('a', '4.99'),
('b', '9.99'),
('c', '1.99'),
('d', '8.99');
select Y.N + 2019 YearNumber, W.WeekNumber, T.Model
from (
-- Cross join 5 * 10, then filter to 52/53 as required
select W1.N * 10 + W2.N + 1 WeekNumber
from (
select 0 N
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
) W1
cross join (
select 0 N
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9
) W2
) W
-- Cross join number of years required, just ensure its more than will ever be needed then filter back
cross join (
select 0 N
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9
) Y
cross join (
-- Assuming only a single row per model is required, and the invoice column can be ignored
select model
from #Test
group by model
) T
-- Some filter to restrict the years
where Y.N <= 3
-- Some filter to restrict the weeks
and W.WeekNumber <= 53
order by YearNumber, WeekNumber;
I created a table to temporary calendar table containing all the weeks and years. To account for leap years, I took the last 7 days of a year and got the ISO week for each day. To know how many weeks are in a year, I put these values into another temp table and took the max value of it. Azure Synapse doesn't support multiple values in one insert so it looks a lot longer than it should be. I also have to declare each as variable since Synapse can only insert literal or variable. I then cross-joined with my ItemInformation table.
CREATE TABLE #temp_dates
(
year SMALLINT,
week TINYINT
);
CREATE TABLE #temp_weeks
(
week_num TINYINT
);
DECLARE #year_counter SMALLINT
SET #year_counter = 2019
DECLARE #week_counter TINYINT
WHILE ( #year_counter <= Datepart(year, Getdate() - 1) + 2 )
BEGIN
SET #week_counter = 1;
DECLARE #day_1 TINYINT
SET #day_1 = Datepart(isowk, Concat('12-25-', #year_counter))
DECLARE #day_2 TINYINT
SET #day_2 = Datepart(isowk, Concat('12-26-', #year_counter))
DECLARE #day_3 TINYINT
SET #day_3 = Datepart(isowk, Concat('12-27-', #year_counter))
DECLARE #day_4 TINYINT
SET #day_4 = Datepart(isowk, Concat('12-28-', #year_counter))
DECLARE #day_5 TINYINT
SET #day_5 = Datepart(isowk, Concat('12-29-', #year_counter))
DECLARE #day_6 TINYINT
SET #day_6 = Datepart(isowk, Concat('12-30-', #year_counter))
DECLARE #day_7 TINYINT
SET #day_7 = Datepart(isowk, Concat('12-31-', #year_counter))
TRUNCATE TABLE #temp_weeks
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_1)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_2)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_3)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_4)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_5)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_6)
INSERT INTO #temp_weeks
(week_num)
VALUES (#day_7)
DECLARE #max_week TINYINT
SET #max_week = (SELECT Max(week_num)
FROM #temp_weeks)
WHILE ( #week_counter <= #max_week )
BEGIN
INSERT INTO #temp_dates
(year,
week)
VALUES ( #year_counter,
#week_counter )
SET #week_counter = #week_counter + 1
END
SET #year_counter = #year_counter + 1
END
DROP TABLE #temp_weeks;
SELECT i.model,
d.year,
d.week
FROM dbo.iteminformation i
CROSS JOIN #temp_dates d
ORDER BY model,
year,
week
DROP TABLE #temp_dates

Pivot Activity Code Days Months

I am trying to get the activity codes for specific days to show the 31 days in every month of the year for a specific staff member.
If the staff member was present, sick, holiday leave, etc... I want those activity codes to display based on the output below for a year act_date range.
Thanks!
Pivot Activity Code Days Months
This can be achieved with pivoting. Here you can enter the staff id in the query to fetch the results for that particular staff.
--create table
create table staff_info
(
staffId int,
actDate datetime,
activityCode int
)
--insert values
insert into staff_info values
(2699, '01/02/2017', 101),
(2699, '05/14/2017', 303),
(2699, '08/06/2017', 101),
(1927, '10/25/2017', 105)
--actual solution
select * from
(
select staffId, day(actDate) as act_day,month(actDate) as actual_month,
activityCode
from staff_info
where staffId=2699 ----- enter the staff id here
) src
pivot
(
sum(activityCode)
for act_day in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],
[14],[15],
[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],
[31]
)
) p
Result:
Firstly, create a function which would give the date values for a specific range
CREATE FUNCTION [dbo].[GetAllDatesBetweenRange]
(
#FromDate DATE
,#ToDate DATE
)
RETURNS #Dates TABLE
(
DateVal DATE
)
AS
BEGIN
;WITH CTE
AS
(
SELECT #FromDate AS FromDate
UNION ALL
SELECT DATEADD(DD,1,FromDate)
FROM CTE
WHERE FromDate < #ToDate
)
INSERT INTO #Dates
SELECT FromDate FROM CTE
OPTION (MAXRECURSION 0)
RETURN;
END
GO
Use the below dynamic query to pivot for the specific date range
DECLARE #Sql NVARCHAR(MAX);
DECLARE #DateVal NVARCHAR(MAX);
SELECT #DateVal = STUFF((SELECT ',['+CAST(DateVal AS NVARCHAR(50))+']'
FROM [dbo].[GetAllDatesBetweenRange]('2017-01-01','2017-12-31')
FOR XML PATH('')),1,1,'')
SET #Sql = '
;WITH CTE
AS
(
SELECT Res1.STAFF_ID
,Res2.DateVal
,Res1.ACTIVITY_CODE
FROM [dbo].[GetAllDatesBetweenRange](''''2017-01-01'''',''''2017-12-31'''') Res1
LEFT JOIN TableA A ON A.ACT_DATE = Res1.DateVal
)
SELECT STAFF_ID
,*
FROM CTE
PIVOT
(
MAX(ACTIVITY_CODE)
FOR DateVal IN ('+#DateVal+')'+'
)'
EXEC SP_EXECUTESQL #Sql

How To Get WeekDay Names Between Two WeekDay Name Fields in SQL Server

I have two columns with weekday names my question is how to get all weekday names between those two weekday names
Ex:
WeekStartDate WeekEndDate
monday friday
I want result like this
Monday, Tuesday, Wednesday, Thursday, Friday
Please help me
Thank you
Could you use the Between Operator in Where Clause and add a INT column as between won't work on nvarchar i guess.
Create table #temp(
data int,
dayN Nvarchar(100)
,dayc int
)
SELECT * FROM #TEMP
Insert into #temp values (10,'Monday',2)
Insert into #temp values (20,'tuesday',3)
Insert into #temp values (30,'wednesday',4)
Insert into #temp values (40,'thursday',5)
Insert into #temp values (50,'friday',6)
SELECT Data
FROM #TEMP
where dayN between 'Monday' and 'Wednesday'
--Above query gives wrong result
SELECT Data
FROM #TEMP
where dayC between 3 and 6
You can do It in following:
declare #tbl table (WeekStartDate nvarchar(60),WeekEndDate nvarchar(60) )
insert into #tbl values ('Monday', 'Friday')
declare #weeknums table (name nvarchar(60), num int)
insert into #weeknums values
('Monday' , 1 ),
('Tuesday' , 2 ),
('Wednesday' , 3 ),
('Thursday' , 4 ),
('Friday' , 5 ),
('Saturday' , 6 ),
('Sunday' , 7 )
declare #min nvarchar(max) = (select min(num)
from #tbl t
join #weeknums w on t.WeekStartDate = w.name or t.WeekEndDate = w.name)
declare #max nvarchar(max) = (select max(num)
from #tbl t
join #weeknums w on t.WeekStartDate = w.name or t.WeekEndDate = w.name)
select w.*
from #weeknums w
where w.num between #min and #max

How to Sum value of Pivoted Columns and add it into another Pivoted Column

I want to sum up all the CountHours and display in pivoted column Total.Total is the sum of all SUnday , monday ... for particular UserName. How to achieve this
?
select FullName,Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Total
from
(Select UserId_Fk,ISNULL(CAST(CountHours as decimal(18,2)),0)as CountHours,[Day] f rom CheckInCheckOut)
as convertedtable
inner join Users
on convertedtable.UserId_Fk=Users.UserId
PIVOT
(
SUM(CountHours)
FOR Day
IN([Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday],[Total])
)
as PivotTable
Result of this query is:
Table Structure:
Table[CheckInCheckOut]
CheckInCheckOutId int
UserId_Fk int
CountHours nvarchar(50)
Day nvarchar(50)
Example would be appreciated.
you should calculate total column field, i.e it is not in list of pivot columns.
Data
create table #CheckInCheckOut(Id int identity(1,1),UserId_Fk int,CountHours varchar(50),[Day] varchar(50))
INSERT INTO #CheckInCheckOut(UserId_Fk,CountHours,[Day]) VALUES
(1,'2','Sunday'),(1,'2','Monday'),(1,'2','Tuesday'),(1,'2','Wednesday'),(1,'2','Thursday'),(1,'2','Friday'),(1,'2','Saturday')
,(2,'3','Sunday'),(2,'3','Monday'),(2,'3','Tuesday'),(2,'3','Wednesday'),(2,'3','Thursday'),(2,'3','Friday'),(2,'3','Saturday')
,(3,'3','Sunday'),(3,'3','Monday'),(3,'3','Tuesday'),(3,'3','Wednesday'),(3,'3','Thursday'),(3,'3','Friday'),(3,'3','Saturday')
create table #Users(UserId int identity(1,1),FullName varchar(50))
INSERT #Users(FullName) values('Abdul'),('khan'),('Tariq')
Query to find total too:
select FullName
,[Sunday] = SUM([Sunday])
,[Monday] = SUM([Monday])
,[Tuesday] = SUM([Tuesday])
,[Wednesday] = SUM([Wednesday])
,[Thursday] = SUM([Thursday])
,[Friday] = SUM([Friday])
,[Saturday] = SUM([Saturday])
, Total= SUM([Sunday]+[Monday]+[Tuesday]+[Wednesday]+[Thursday]+[Friday]+[Saturday])
from
(Select UserId_Fk,ISNULL(CAST(CountHours as decimal(18,2)),0)as CountHours,[Day]
from #CheckInCheckOut)
as convertedtable
inner join #Users
on convertedtable.UserId_Fk=#Users.UserId
PIVOT
(
SUM(CountHours)
FOR Day
IN([Sunday],[Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday])
)
as PivotTable
GROUP BY FullName
Output
Also if u want total horizontal and vertical both then replace:
--GROUP BY FullName
GROUP BY ROLLUP(FullName);
For more follow link https://stackoverflow.com/a/17142530/1915855
DROP TABLE #CheckInCheckOut
DROP TABLE #Users
Try This Way here is the example.
Create a Table
CREATE TABLE cars
(
car_id tinyint,
attribute varchar(20),
value varchar(20),
sumd decimal(18,2)
)
Insert Values to it
insert into cars(car_id, attribute, value, sumd)
values (1, 'Make', 'VW',1),
(1, 'Model', 'Rabbit',2),
(1, 'Color', 'Gold',3),
(2, 'Make', 'Jeep',4),
(2, 'Model', 'Wrangler',5),
(2, 'Color', 'Gray',6)
For Making the Total
declare #Columns2 VARCHAR(8000)
declare #Sql VARCHAR(4000)
declare #Columns VARCHAR(8000)
SET #Columns = substring((select distinct ',['+attribute+']' from cars group by attribute for xml path('')),2,8000)
SET #Columns2 = substring((select distinct ',IsNull(['+attribute+'],0) as ['+attribute+']' from cars group by attribute for xml path('')),2,8000)
print #Columns
print #Columns2
SET #SQL = 'SELECT car_id, '+#Columns2+', total
FROM
(Select car_id,attribute, SUM(sumd) OVER (PARTITION BY attribute) as total
, sumd from cars) SourceData
PIVOT
(sum(sumd) for attribute in ('+#Columns+')) pivottable
Order by car_id '
exec(#sql)

SQL counting total rows from temporary table in a table valued function

I need the number of total rows inserted in #t, if #total equals 1.
How can I do that?
ALTER FUNCTION [dbo].[myfunction]
(
#ID int = NULL,
#years int = NULL,
,#total BIT = 0
)
RETURNS #t TABLE (
RowNum int,
ID int,
years int,
)
AS
BEGIN
INSERT INTO #t
SELECT
ROW_NUMBER() OVER(ORDER BY years) AS RowNum,
ID,
years,
FROM dbo.mytable
WHERE ..
RETURN
END
The look of the results should be:
Total RowNum ID year
-------------------------------------
3 1 101 2014
3 2 102 2015
3 3 103 2016
Thanks!
RETURNS #t TABLE (
Total int,
RowNum int,
ID int,
years int,
)
...
INSERT INTO #t
SELECT
NULL,
ROW_NUMBER() OVER(ORDER BY years) AS RowNum,
ID,
years,
FROM dbo.mytable
WHERE ..
...
IF(#total=1) BEGIN
DECLARE #Count INT
SELECT #Count=COUNT(*) FROM #t
UPDATE #t SET Total=#Count
END
RETURN

Resources