Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 20 days ago.
Improve this question
Would like to have three years performance report. Sample data is below. No date before June 2022 while report need to be all three years. The report will be live report. It will be 2022, 2023 and 2024 when report is generated in 2024.
Data
Year Month Amount
2022 07 2207
2022 08 2208
2022 09 2209
2022 10 2210
2022 11 2211
2022 12 2212
2023 01 2301
2023 02 2302
Report
2021 2022 2023
01 Null Null 2301
02 Null Null 2302
03 Null Null Null
04 Null Null Null
05 Null Null Null
06 Null Null Null
07 Null 2207 Null
08 Null 2208 Null
09 Null 2209 Null
10 Null 2210 Null
11 Null 2211 Null
12 Null 2212 Null
With John' solution, below is code and result.
create table temp
(
[Year] CHAR(4),
[Month] CHAR(2),
amount money
)
insert into temp values ('2022', '07', 2207)
insert into temp values ('2022', '08', 2208)
insert into temp values ('2022', '09', 2209)
insert into temp values ('2022', '10', 2210)
insert into temp values ('2022', '11', 2211)
insert into temp values ('2022', '12', 2212)
insert into temp values ('2023', '01', 2301)
insert into temp values ('2023', '02', 2302)
Declare #SQL varchar(max) = '
Select *
From (
Select Item = [Year]
,[Month]
,Amount
From temp
Where [Year]>=year(getdate())-2
Union All
Select Item = year(getdate())
,[Month]
,Amount = null
From ( values (''01''),(''02''),(''03''),(''04''),(''05''),(''06''),(''07''),(''08''),(''09''),(''10''),(''11''),(''12'') ) A([Month])
) src
Pivot ( sum(Amount) for item in ('+ quotename(year(getdate())-2)+','+quotename(year(getdate())-1)+','+quotename(year(getdate())-0) +') ) pvt
'
Exec(#SQL)
drop table temp
Result:
Dynamic columns require dynamic SQL.
Note the UNION ALL to "fill-in" the missing months
Here is a working option
Declare #SQL varchar(max) = '
Select *
From (
Select Item = year
,Month
,Amount
From YourTable
Where year>=year(getdate())-2
Union All
Select Item = year(getdate())
,Month
,Amount = null
From ( values (''01''),(''02''),(''03''),(''04''),(''05''),(''06''),(''07''),(''08''),(''09''),(''10''),(''11''),(''12'') ) A(Month)
) src
Pivot ( sum(Amount) for item in ('+ quotename(year(getdate())-2)+','+quotename(year(getdate())-1)+','+quotename(year(getdate())-0) +') ) pvt
'
Exec(#SQL)
Results
Related
I have a calendar type check I'm trying to do on SQL Server. For each month of the year, I need to check if the employee was hired or not. There can be an original hire date, a rehire date, a termination date, and the last termination date; other than the original hire date, which will always have a value, all of these date fields can be null.
Given the following data:
EmpID OrigHireDate TermDate LastTermDate RehireDate
42 2017-09-25 NULL 2019-03-26 2019-10-30
What I am trying to achieve is the following result for each month for last year (i.e. 2019) and having no luck in coming up with the right statement. Assume I already have a table containing each month's number along with the start/end date of the month that I can use to compare the date ranges.
EmpID Month EmployeeDuring
42 1 True
42 2 True
42 3 True
42 4 False
42 5 False
42 6 False
42 7 False
42 8 False
42 9 False
42 10 True
42 11 True
42 12 True
The following works. May need some minor adjustments to handle all possible combinations of EmpID, OrigHireDate, TermDate, LastTermDate, RehireDate
I apologize for posting mostly code. Will add more explanation and or comments tomorrow.
DECLARE #EmpID int, #OrigHireDate date, #TermDate date, #LastTermDate date, #RehireDate date
DECLARE #year int
SET #year = 2019
SET #EmpID = 42
SET #OrigHireDate = '2017-09-25'
SET #TermDate = NULL
SET #LastTermDate = '2019-03-26'
SET #RehireDate = '2019-10-30'
SET #OrigHireDate = DATEADD(day,-DAY(#OrigHireDate)+1, #OrigHireDate)
SET #LastTermDate = DATEADD(day,-DAY(ISNULL(#LastTermDate,GETDATE()))+1, #LastTermDate)
SET #RehireDate = DATEADD(day,-DAY(#RehireDate)+1, #RehireDate)
SET #TermDate = DATEADD(day,-DAY(ISNULL(#TermDate,GETDATE()))+1, #TermDate)
;WITH CTE_DATES_ORIGINAL([Date],[Level])
AS
(
SELECT #OrigHireDate AS [DATE],
1 AS [Level]
UNION ALL
SELECT
DATEADD(MONTH,1, [DATE] ) , [Level] + 1
FROM CTE_DATES_ORIGINAL
WHERE [DATE] < ISNULL(#LastTermDate,GETDATE())
),
CTE_DATES_REHIRE([Date],[Level])
AS
(
SELECT #RehireDate AS [DATE],
1 AS [Level]
UNION ALL
SELECT
DATEADD(MONTH,1, [DATE] ) , [Level] + 1
FROM CTE_DATES_REHIRE
WHERE [DATE] < ISNULL(#TermDate,GETDATE())
),
CTE_DATES_YEAR(m) AS
(
SELECT 1
UNION ALL
SELECT m+1
FROM CTE_DATES_YEAR
WHERE m < 12
)
SELECT #EmpID AS EmpID, m AS [Month], ISNULL(EmployeeDuring.EmployeeDuring,0) AS EmployeeDuring
FROM CTE_DATES_YEAR y
LEFT OUTER JOIN
(
SELECT
[Date], 1 AS EmployeeDuring
FROM
CTE_DATES_ORIGINAL
UNION
SELECT
[Date] , 1 AS EmployeeDuring
FROM
CTE_DATES_REHIRE
) employeeDuring
ON DATEADD(month,m-1, CAST(CAST(#year AS CHAR(4)) + '-1-1' AS DATE)) = employeeDuring.[Date]
ORDER BY m
OPTION (MAXRECURSION 5000)
This question already has answers here:
SQL Date Query include month even if no data
(2 answers)
Closed 3 years ago.
I am using the below DDL to show sales data for months in 2018 and 2019. My issue is that obviously we will have no data for Sept - Dec 2019, but I need that to be returned and show a 0 amount for it.
How do I need to edit this query so that I have all months 1 - 12 for both 18 and 19 returned?
CREATE TABLE PrevYear (
[EmployeeNumber] char(8) NOT NULL,
[SaleAmount] int DEFAULT NULL,
[SaleDate] date NOT NULL,
[EmployeeName] char(17) NOT NULL
);
CREATE TABLE CurrentYear (
[EmployeeNumber] char(8) NOT NULL,
[SaleAmount] int DEFAULT NULL,
[SaleDate] date NOT NULL,
[EmployeeName] char(17) NOT NULL
);
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-01-10', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ea12', '199', '2019-01-13', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-03-01', 'Sam Smith');
INSERT INTO CurrentYear
VALUES ('ls22', '100', '2019-05-01', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-01-10', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '199', '2018-01-13', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-03-01', 'Sam Smith');
INSERT INTO PrevYear
VALUES ('ls22', '100', '2018-05-01', 'Sam Smith');
My desired output from the query is:
Jan 18 $1
Jan 19 $1
Feb 18 $1
Feb 19 $1
Mar 18 $1
Mar 19 $1
Apr 18 $1
Apr 19 $1
May 18 $1
May 19 $1
Jun 18 $1
Jun 19 $1
Jul 18 $1
Jul 19 $1
Aug 18 $1
Aug 19 $1
Sep 18 $1
Sep 19 $0
Oct 18 $1
Oct 19 $0
Nov 18 $1
Nov 19 $0
Dec 18 $1
Dec 19 $0
This is the query I had
SELECT Month, Sum(ia) AS SaleAmount
FROM (SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(SaleAmount) AS IA
FROM CurrentYear
WHERE employeename = 'Sam Smith'
GROUP BY Date_format(saledate, '%m-%Y'),
employeename
UNION ALL
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(SaleAmount) AS IA
FROM PrevYear
WHERE employeename = 'Sam Smith'
GROUP BY Date_format(saledate, '%m-%Y'),
employeename) previousQuery
GROUP BY month
ORDER BY month
You can simply add a lookup table with month that will contains 12 records (one for each month)
The structure will be: id, description.
On existing tables you can refers month by id: less space more performance in join.
On your query using left/right join you will always have the missing month.
PS: need only one table to manage this, and adding a simple where condition (or group by condition) where needed for years management
I have a table of data which i am using a count statement to get the amount of records for the submission date
example
AuditId Date Crew Shift Cast ObservedBy 2ndObserver AuditType Product
16 2017-06-27 3 Day B1974, B1975 Glen Mason NULL Identification Billet
20 2017-06-29 1 Day 9879 Corey Lundy NULL Identification Billet
21 2017-06-29 4 Day T9627, T9625 Joshua Dwyer NULL ShippingPad Tee
22 2017-06-29 4 Day NULL Joshua Dwyer NULL Identification Billet
23 2017-06-29 4 Day S9874 Joshua Dwyer NULL ShippingPad Slab
24 2017-06-29 4 Day Bay 40 Joshua Dwyer NULL Identification Billet
Basically I am using the following code to get my results
SELECT YEAR([Date]) as YEAR, CAST([Date] as nvarchar(25)) AS [Date], COUNT(*) as "Audit Count"
FROM AuditResults
where AuditType = 'Identification' AND Product = 'Billet'
group by Date
this returns example
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-29 3
Now I want to be able to retrieve all dates even if blank
so I would like the return to be
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-28 0
2017 2017-06-29 3
I have the following function I am trying to use:
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #DateList TABLE (Dt date)
AS
BEGIN
DECLARE #TotalDays int, #DaysCount int
SET #TotalDays = DATEDIFF(dd,#FromDate,#ToDate)
SET #DaysCount = 0
WHILE #TotalDays >= #DaysCount
BEGIN
INSERT INTO #DateList
SELECT (#ToDate - #DaysCount) AS DAT
SET #DaysCount = #DaysCount + 1
END
RETURN
END
How do I use my select statement with this function? or is there a better way?
cheers
Try this;
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #YourData TABLE ([Year] int, DateText nvarchar(25),[Audit Count] int)
AS
begin
insert into #YourData
SELECT
YEAR(allDates.[Date]) as YEAR,
CAST(allDates.[Date] as nvarchar(25)) AS [Date],
COUNT(r.Product) as "Audit Count"
from
(
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
)allDates
left join
AuditResults r on r.[Date]=allDates.[date] and r.AuditType = 'Identification' AND r.Product = 'Billet'
where
allDates.[Date]>=#FromDate and allDates.[Date]<=#ToDate
group by
allDates.[Date]
return
end
The key is the 'allDates' section ;
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
This will return all dates between 1900 and 2173 (in this example). Limit that as you need but a nice option. A ton of different ways to approach this clearly
you have to create another table calendar as (Mysql)- idea is the same on all RDBMS-
CREATE TABLE `calendar` (
`dt` DATE NOT NULL,
UNIQUE INDEX `calendar_dt_unique` (`dt`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
and fill with date data.
more details
I have a SQL table which stores the Status and Location of employees
empID date status location
---------------------------------------------------------
001 01.01.2014 1 1
001 02.01.2014 2 1
001 04.01.2014 1 3
....
055 01.01.2014 3 3
055 02.01.2014 4 2
Now I want to create a list with the employee id and days 1 to 31 as columns
and a concatenation of the Status and Location as the value. If I take max() of the status, it works, however, the location information is missing. If I try to do
SELECT *
FROM (SELECT (empId), left(datename(dd,[Date]),2)as [day_date], [Status] as status, [Location] as location, [Status] + '|' + [Location] as val FROM [dbo].[table]
WHERE [date] BETWEEN '2014-01-01' AND '2014-01-30') as s
PIVOT (MAX(val) FOR [day_date] 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])
)AS p
order by empID
it fails because I can't use max() against a concatenated string.
My goal is:
empID 01 02 03 04 .... 30 31
---------------------------------------------------------
001 1|1 2|1 null 1|3 null null
055 3|4 4|2 null null null null
Cast [Status] and [Location] to a varchar performing the concatenation. I have arbitrarily chosen 3 as the length of the varchar but that may vary depending on what your maximum possible [Status] and [Location] are.
Also, remove the [Status] and [Location] columns from the inner query.
SELECT *
FROM (
SELECT
empId,
left(datename(dd,[Date]),2)as [day_date],
cast([Status] AS varchar(3)) + '|' + cast([Location] as varchar(3)) as val
FROM [dbo].[table]
WHERE [date] BETWEEN '2014-01-01' AND '2014-01-30') as s
PIVOT (MAX(val) FOR [day_date] 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])
)AS p
order by empID
I'm pretty sure I need a pivot to do this but can't quite figure it out. (sql newb)
I have data like this:
ID CompanyID Year Revenue Expenses
1 0003 2011 12000 4000
2 0003 2010 9000 6000
3 0003 2009 7000 9000
4 0010 2011 134300 34000
5 0010 2010 43000 46000
6 0010 2009 73000 39000
Can I use a Pivot to display this table like this:
CompanyID 2011-Revenue 2010-Revenue 2009-Revenue 2011-Expenses 2010-Expenses 2009-Expenses
0003 12000 9000 7000 4000 6000 9000
0010 134300 43000 73000 34000 46000 39000
Here is what I have so far...
SELECT P1.*
FROM (SELECT [CompanyID]
,CASE P.[Year] WHEN 2011 THEN P.[Revenue] ELSE NULL END AS '2011-Revenue'
,CASE P.[Year] WHEN 2010 THEN P.[Revenue] ELSE NULL END AS '2010-Revenue'
FROM tblRecords P WHERE P.[CompanyID] = #companyID GROUP BY CompanyID, [Year], [Revenue]) AS P1
Which is returning:
CompanyID 2011-Revenue 2010-Revenue
0003 12000 NULL
0003 NULL 9000
Few problems with my results...
The there is two records for CompanyID 0003 I'd like it to group into one record
I can only choose 1 company at a time, I need to choose multiple. I tried
FROM tblRecords P
WHERE P.[CompanyID] IN (#CompanyIDs)
GROUP BY CompanyID, [Year], [Revenue]) AS P1
Where #CompanyIDs is a string like '0003, 0010' - It didn't fail but the result was just an empty table with the headers and no data..
Any help would be appreciated.. or let me know if I'm misunderstanding pivot?
Thanks a lot!
Thomas
EDIT: Using Microsoft SQL Server Management Studio 2005 Express
UPDATE 2: I've figured out joining tables for more details however I still need to be able to pass in the the CompanyIDs as a comma delimited string.. any help on that would be appreciated.
vvvvvvvv I'VE FIGURED OUT BELOW THIS (will post once all is working) vvvvvvv
UPDATE: It looks like what Ruben has proposed is going to work however I've just determined I need a bit more functionality to this... Can I join this with another table to have the headers
CompanyID CompanyName CompanyAddress 2011-Revenue 2010-Revenue
Where CompanyName and CompanyAddress come from another table (tblCompanyDetails)
I've tried using:
SELECT *
FROM
(
SELECT CompanyID, tblCompanyDetails.CompanyName, tblCompanyDetails.CompanyAddress, CAST(YEAR AS varchar) + ' - Revenue' Type,
Revenue Value FROM tblRecords
FROM tblRecords INNER JOIN tblCompanyDetails ON tblRecords.CompanyID = tblCompanyDetails.CompanyID
UNION ALL
SELECT CompanyID, tblCompanyDetails.CompanyName, tblCompanyDetails.CompanyAddress, CAST(YEAR AS varchar) + ' - Expenses' Type,
Expenses Value FROM tblRecords
FROM tblRecords INNER JOIN tblCompanyDetails ON tblRecords.CompanyID = tblCompanyDetails.CompanyID
) src
PIVOT
(
SUM(Value) for [Type] in
([2011 - Revenue], [2010 - Revenue], [2009 - Revenue],
[2011 - Expenses], [2010 - Expenses], [2009 - Expenses]
)
) pvt
WHERE CompanyID = #CompanyID
I get the error:
Msg 1038, Level 15, State 4, Procedure spCompare, Line 10
An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Add a name or single space as the alias name.
Try this:
DECLARE #CompanyIDs XML
DECLARE #Records TABLE
(
ID int,
CompanyID char(4),
Year int,
Revenue decimal,
Expenses decimal
)
DECLARE #CompanyDetails TABLE
(
CompanyID char(4),
Name varchar(50),
Address varchar(50)
)
SET #CompanyIDs = '
<filter>
<CompanyID>0003</CompanyID>
<CompanyID>0010</CompanyID>
</filter>'
INSERT INTO #Records
(ID, CompanyID, Year, Revenue, Expenses)VALUES
(1, '0003', 2011, 12000 , 4000 ),
(2, '0003', 2010, 9000 , 6000 ),
(3, '0003', 2009, 7000 , 9000 ),
(4, '0010', 2011, 134300, 34000),
(5, '0010', 2010, 43000 , 46000),
(6, '0010', 2009, 73000 , 39000)
INSERT INTO #CompanyDetails
(CompanyID, Name, Address) VALUES
('0003', 'Company A', 'A Street'),
('0010', 'Company B', 'B Street')
SELECT *
FROM
(
SELECT CompanyID, CAST(YEAR AS varchar) + ' - Revenue' Type,
Revenue Value FROM #Records
UNION ALL
SELECT CompanyID, CAST(YEAR AS varchar) + ' - Expenses' Type,
Expenses Value FROM #Records
) src
PIVOT
(
SUM(Value) for [Type] in
([2011 - Revenue], [2010 - Revenue], [2009 - Revenue],
[2011 - Expenses], [2010 - Expenses], [2009 - Expenses]
)
) pvt
JOIN #CompanyDetails Details
ON Details.CompanyId = pvt.CompanyID
WHERE pvt.CompanyID IN
(
SELECT T.C.value('.', 'char(4)')
FROM #CompanyIDs.nodes('/filter/CompanyID') T(C)
)
You need to send your company filter as XML and use that subselect to break your data as required for pivot operations. For JOIN operations, just use pvt output