How to speed up the query? - sql-server

Here's my query (it's a part of stored procedure):
declare #sql varchar(4000)
declare #tab1 table (ActionID int, ActionDate datetime, [Group] varchar(10), GroupUnicode int, GroupList varchar(100), Artist varchar(250), PlaceName varchar(250),
ActionType varchar(25), ActionTypeID smallint, ActionPlaceID int, Picture varchar(512), SearchWords varchar(250), ActionEndDate datetime, StrEndDate varchar(250),
NameForH1 nvarchar(1000), DatesAsPeriod tinyint, DateList varchar(4000))
set #Date2 = DATEADD(day, 1, #Date2)
insert into #tab1(ActionID, ActionDate, Artist, PlaceName, ActionTypeID, ActionPlaceID, SearchWords, ActionEndDate, StrEndDate, NameForH1, DatesAsPeriod, DateList)
select X.ID, convert(datetime, SUBSTRING(X.DateList, 1, 19), 120), X.Artist, PlaceName, X.ActionTypeID, X.ActionPlaceID, X.SearchWords, ActionEndDate,
case when ActionEndDate IS NOT NULL AND convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) < ActionEndDate
then ' - ' + convert(varchar, ActionEndDate, 104) + ' ' + convert(varchar(5), ActionEndDate, 108)
else '' end as StrEndDate, X.NameFOrH1, X.DatesAsPeriod, X.DateList
from (
SELECT DISTINCT
TOP (100) PERCENT a.ID, a.Artist, a.DateBeg, a.DateEnd, ap.PlaceName, a.ProviderID, a.SubSiteID, a.ActionPlaceID,
a.CounterAgentID, a.ActionTypeID, a.GenreTypeID, a.ShowTypeID AS ActionShowTypeID, ad.ShowTypeID, ap.CityID, a.ActionPlaceGroupID,
a.ActionTopTypeID, a.Canceled, a.ETicketEnabled, st.ShowOnMainPage AS ActionShowOnMainPage, st.ShowInActionList AS ActionShowInActionList,
st.ShowInCashdesk AS ActionShowInCashdesk, st1.ShowOnMainPage, st1.ShowInActionList, st1.ShowInCashdesk, a.SearchWords, a.AutoGenerate,
ad.ActionEndDate, a.NameForH1, a.CommonActionId, tca.Name AS CommonActionName, a.AgeRestrictionsId, a.DatesAsPeriod,
dbo.fn_ConvertActionDatesToDateList(a.ID) AS DateList
FROM dbo.T_Action AS a INNER JOIN
dbo.T_ActionDates AS ad ON a.ID = ad.ActionID INNER JOIN
dbo.T_ActionPlace AS ap ON a.ActionPlaceID = ap.ID INNER JOIN
dbo.T_ShowType AS st ON a.ShowTypeID = st.ID LEFT OUTER JOIN
dbo.T_ShowType AS st1 ON ad.ShowTypeID = st1.ID LEFT OUTER JOIN
dbo.T_CommonAction AS tca ON tca.ID = a.CommonActionId
WHERE (a.Visible = 1)) AS X INNER JOIN dbo.T_Action AS a ON X.ID = a.ID
where (ActionShowInActionList = 1) and (ShowInActionList = 1)
and ((X.Artist like '%' + #Artist + '%') or (X.SearchWords like '%' + #Artist + '%'))
and X.ActionTypeID = case when #ActionTypeID > 0 then #ActionTypeID else X.ActionTypeID end
and X.ActionTopTypeID = case when #ActionTopTypeID > 0 then #ActionTopTypeID else X.ActionTopTypeID end
and X.GenreTypeID = case when #GenreTypeID > 0 then #GenreTypeID else X.GenreTypeID end
and X.ActionPlaceID = case when #ActionPlaceID > 0 then #ActionPlaceID else X.ActionPlaceID end
and CityID = case when #CityID > 0 then #CityID else CityID end
and X.ActionPlaceGroupID = case when #GroupPlaceID > 0 then #GroupPlaceID else X.ActionPlaceGroupID end
and ((convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) = '1980-01-01')
or ((convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) >= #Date1 OR ActionEndDate >= #Date1) AND convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) < #Date2))
order by
case when #SortBy = 'byPlace' then PlaceName end,
case when #SortBy = 'byDate' or #SortBy = 'byPlace' then case when convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) = '1980-01-01 00:00' then DATEADD(YY, 1000, convert(datetime, SUBSTRING(X.DateList, 1, 19), 120)) else convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) end end, Artist,
case when #SortBy = 'byAlphabet' then convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) end
As you can see, there is nested query and there is the following construction:
convert(datetime, SUBSTRING(X.DateList, 1, 19), 120)
that repeats several times.
Also, there is the function dbo.fn_ConvertActionDatesToDateList(a.ID):
ALTER FUNCTION [fn_ConvertActionDatesToDateList]
(
#actionId int
)
RETURNS varchar(4000)
AS
BEGIN
declare #dateList varchar(4000)
select distinct #dateList = STUFF(CAST((
SELECT [text()] = ', ' + convert(varchar, ActionDate, 120)
FROM T_ActionDates where ActionID = a.ID and (ActionDate = '1980-01-01' or ActionDate > GETDATE())
FOR XML PATH(''), TYPE) AS VARCHAR(8000)), 1, 2, '') from T_Action as a join T_ActionDates as ad on a.ID = ad.ActionID
where (ad.ActionDate = '1980-01-01' or ad.ActionDate > GETDATE()) and Visible = 1 and CloseForCorrect = 0 and a.ID = #actionId
return #dateList
END
How can I speed it up?

I really can't tell you exactly what to do to fix the issue, but I can point out the spots that are almost certainly causing slow down.
Ensure your columns in all the joins and WHERE clauses are indexed. There is no way for anyone here to know what is and is not indexed unless you tell us.
The sub-select has TOP (100) PERCENT. You should not need this.
You are generating a likely large sub-set of data by having the sub-select without the improving the WHERE clause in the sub-select. It looks like the WHERE in the main select should be moved into the subselect.
Get rid of the sub-select.
With those things said, try this:
declare #sql varchar(4000)
declare #tab1 table (ActionID int, ActionDate datetime, [Group] varchar(10), GroupUnicode int, GroupList varchar(100), Artist varchar(250), PlaceName varchar(250),
ActionType varchar(25), ActionTypeID smallint, ActionPlaceID int, Picture varchar(512), SearchWords varchar(250), ActionEndDate datetime, StrEndDate varchar(250),
NameForH1 nvarchar(1000), DatesAsPeriod tinyint, DateList varchar(4000))
set #Date2 = DATEADD(day, 1, #Date2)
insert into #tab1(ActionID, ActionDate, Artist, PlaceName, ActionTypeID, ActionPlaceID, SearchWords, ActionEndDate, StrEndDate, NameForH1, DatesAsPeriod, DateList)
select
X.ID, convert(datetime, SUBSTRING(X.DateList, 1, 19), 120), X.Artist, PlaceName, X.ActionTypeID, X.ActionPlaceID, X.SearchWords, ActionEndDate,
case
when ActionEndDate IS NOT NULL AND convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) < ActionEndDate
then ' - ' + convert(varchar, ActionEndDate, 104) + ' ' + convert(varchar(5), ActionEndDate, 108)
else ''
end as StrEndDate
, X.NameFOrH1, X.DatesAsPeriod, X.DateList
from (
SELECT DISTINCT
a.ID, a.Artist, a.DateBeg, a.DateEnd, ap.PlaceName, a.ProviderID, a.SubSiteID, a.ActionPlaceID,
a.CounterAgentID, a.ActionTypeID, a.GenreTypeID, a.ShowTypeID AS ActionShowTypeID, ad.ShowTypeID, ap.CityID, a.ActionPlaceGroupID,
a.ActionTopTypeID, a.Canceled, a.ETicketEnabled, st.ShowOnMainPage AS ActionShowOnMainPage, st.ShowInActionList AS ActionShowInActionList,
st.ShowInCashdesk AS ActionShowInCashdesk, st1.ShowOnMainPage, st1.ShowInActionList, st1.ShowInCashdesk, a.SearchWords, a.AutoGenerate,
ad.ActionEndDate, a.NameForH1, a.CommonActionId, tca.Name AS CommonActionName, a.AgeRestrictionsId, a.DatesAsPeriod,
dbo.fn_ConvertActionDatesToDateList(a.ID) AS DateList
FROM dbo.T_Action AS a
INNER JOIN dbo.T_ActionDates AS ad ON a.ID = ad.ActionID
INNER JOIN dbo.T_ActionPlace AS ap ON a.ActionPlaceID = ap.ID
INNER JOIN dbo.T_ShowType AS st ON a.ShowTypeID = st.ID
LEFT OUTER JOIN dbo.T_ShowType AS st1 ON ad.ShowTypeID = st1.ID
LEFT OUTER JOIN dbo.T_CommonAction AS tca ON tca.ID = a.CommonActionId
WHERE a.Visible = 1
AND ActionShowInActionList = 1
and (ShowInActionList = 1)
and (
(Artist like '%' + #Artist + '%')
or (SearchWords like '%' + #Artist + '%')
)
and ActionTypeID = case when #ActionTypeID > 0 then #ActionTypeID else ActionTypeID end
and ActionTopTypeID = case when #ActionTopTypeID > 0 then #ActionTopTypeID else ActionTopTypeID end
and GenreTypeID = case when #GenreTypeID > 0 then #GenreTypeID else GenreTypeID end
and ActionPlaceID = case when #ActionPlaceID > 0 then #ActionPlaceID else ActionPlaceID end
and CityID = case when #CityID > 0 then #CityID else CityID end
and ActionPlaceGroupID = case when #GroupPlaceID > 0 then #GroupPlaceID else ActionPlaceGroupID end
and (
convert(datetime, SUBSTRING(DateList, 1, 19), 120) = '1980-01-01'
or (
(
convert(datetime, SUBSTRING(DateList, 1, 19), 120) >= #Date1
OR ActionEndDate >= #Date1
)
AND convert(datetime, SUBSTRING(DateList, 1, 19), 120) < #Date2
)
)
) AS X
order by
case when #SortBy = 'byPlace' then PlaceName end,
case when #SortBy = 'byDate' or #SortBy = 'byPlace' then case when convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) = '1980-01-01 00:00' then DATEADD(YY, 1000, convert(datetime, SUBSTRING(X.DateList, 1, 19), 120)) else convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) end end, Artist,
case when #SortBy = 'byAlphabet' then convert(datetime, SUBSTRING(X.DateList, 1, 19), 120) end

Related

Generate free time between 2 dates in blocks of length depending on task

I'm trying to generate free blocks for workers and work fine if the working hours are like 08:00 to 16:00. But when I change the working hours to 07:30 to 15:30 it doesn't act as I want it to. If my task length is 1 hour/60 minutes, the first available time will be 8.00, which was supposed to be 7:30.
Any suggestions about how I can resolve this issue?
Output:
expected to be starting at 7:30, 8:30 etc
stored procedure
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ProcedureGenerateFreetimeForOneStakeholder]
(
#EmployeeId uniqueidentifier,
#NumberOfDaysBeforeBooking int,
#NumberOfDays int,
#Rebuild int
)
AS
DECLARE #workHours TABLE (
[CompanyKeyValuePairSettingsId] uniqueidentifier,
[Value] nvarchar(249)
)
DECLARE #CompanyId uniqueidentifier = (select top 1 CompanyId from tblStakeholder where StakeholderId = #EmployeeId)
SET #NumberOfDaysBeforeBooking =(select [value] FROM tblCompanyToCompanyKeyValuePairSettings WHERE CompanyId = #CompanyId AND CompanyKeyValuePairSettingsId = '06B546A9-C47E-4A8B-AE13-E3F9F0C87DEC')
SET #NumberOfDays =(select [value] FROM tblCompanyToCompanyKeyValuePairSettings WHERE CompanyId = #CompanyId AND CompanyKeyValuePairSettingsId = '87C6406F-BC47-4F42-B58B-450C9F4EBC9E')
-- Insert user work time to #workHours
INSERT INTO #workHours ([CompanyKeyValuePairSettingsId], [Value]) (SELECT s.[CompanyKeyValuePairSettingsId], s.[Value] FROM [dbo].[tblCompanyToCompanyKeyValuePairSettings] as s
INNER JOIN [dbo].[tblCompanyKeyValuePairSettings] as p on s.[CompanyKeyValuePairSettingsId] = p.CompanyKeyValuePairSettingsId
WHERE p.[Section] = 'workingHours' and [CompanyId] = #CompanyId and p.[CompanyKeyValuePairSettingsId] not in (
SELECT [IsOverwriteTo]
FROM [dbo].[tblCompanyUserKeyValuePairSettings]
INNER JOIN [dbo].[tblCompanyUserToStakeholderIdKeyValuePairSettings] on [tblCompanyUserKeyValuePairSettings].[CompanyUserKeyValuePairSettingsId] = [tblCompanyUserToStakeholderIdKeyValuePairSettings].[CompanyUserKeyValuePairSettingsId]
WHERE Section = 'WorkingHours' AND IsOverwriteTo is not null and StakeholderId = #EmployeeId))
INSERT INTO #workHours ([CompanyKeyValuePairSettingsId], [Value] ) (SELECT [IsOverwriteTo], [tblCompanyUserToStakeholderIdKeyValuePairSettings].[Value]
FROM [dbo].[tblCompanyUserKeyValuePairSettings]
INNER JOIN [dbo].[tblCompanyUserToStakeholderIdKeyValuePairSettings] on [tblCompanyUserKeyValuePairSettings].[CompanyUserKeyValuePairSettingsId] = [tblCompanyUserToStakeholderIdKeyValuePairSettings].[CompanyUserKeyValuePairSettingsId]
WHERE Section = 'WorkingHours' AND IsOverwriteTo is not null and StakeholderId = #EmployeeId)
declare #TimeSlots table (
StartTime [nchar](5) NOT NULL,
EndTime [nchar](5) NOT NULL
)
DECLARE #Today DateTime = CONVERT (date, DATEADD(day, 0, GETDATE()))
DECLARE #FirstDay DateTime = CONVERT (date, DATEADD(day, #NumberOfDaysBeforeBooking, GETDATE()))
DECLARE #Last DateTime = CONVERT (date, DATEADD(day, #NumberOfDays,GETDATE()))
DECLARE #current DateTime = CONVERT (date, DATEADD(day, 0, GETDATE()))
DECLARE #MinuteBeginDay int
DECLARE #MinuteEndDay int
DECLARE #MinuteAssignmentBegin int
DECLARE #MinuteAssignmentEnd int = 1440;
if(#Rebuild = 1)
Begin
DELETE FROM [dbo].[tblEmployeeFreetime] WHERE [StakeholderId] = #EmployeeId
End
if(#Rebuild = 0)
Begin
DELETE FROM [dbo].[tblEmployeeFreetime] WHERE [StakeholderId] = #EmployeeId and [StartTime] <= #FirstDay
End
-- Insert blocked time
WHILE (#current <= #FirstDay)
BEGIN
INSERT INTO #TimeSlots (StartTime,EndTime) values ('23:59','23:59')
set #MinuteBeginDay = (select CONVERT(INT, (SELECT SUBSTRING((select top (1) StartTime from #TimeSlots), 1, 2))) * 60 + CONVERT(INT, (SELECT SUBSTRING((select top (1) StartTime from #TimeSlots), 4, 5))))
set #MinuteEndDay = (select CONVERT(INT, (SELECT SUBSTRING((select top (1) EndTime from #TimeSlots), 1, 2))) * 60 + CONVERT(INT, (SELECT SUBSTRING((select top (1) EndTime from #TimeSlots), 4, 5))))
set #MinuteAssignmentBegin = 30
While (#MinuteAssignmentBegin < #MinuteAssignmentEnd - #MinuteAssignmentBegin)
Begin
INSERT INTO [dbo].[tblEmployeeFreetime] ([StakeholderId], StartTime,EndTime,[AssignmentLength])
(select #EmployeeId, #current , DATEADD(minute, #MinuteEndDay, #current),#MinuteAssignmentBegin)
SET #MinuteAssignmentBegin = #MinuteAssignmentBegin + 30
End
DELETE FROM #TimeSlots
Set #current = CONVERT (date, DATEADD(day, 1, #current))
End
-- Insert free time on workdays and weekends
SET #current = CONVERT (date, DATEADD(day, 1, (SELECT TOP (1) [EndTime] FROM [dbo].[tblEmployeeFreetime] where [StakeholderId] = #EmployeeId order by [EndTime] desc)))
Declare #SetTime datetime
while (#current <= #Last)
Begin
declare #str VARCHAR(MAX)
set #str = FORMAT(#current, 'dddd')
-----
declare #StartSlot nvarchar(5) = (SELECT w.Value FROM #workHours as w
INNER JOIN tblCompanyKeyValuePairSettings as s on w.[CompanyKeyValuePairSettingsId] = s.[CompanyKeyValuePairSettingsId] where s.Name Like #str + 'StartTime%')
declare #EndSlot nvarchar(5) = (SELECT w.Value FROM #workHours as w
INNER JOIN tblCompanyKeyValuePairSettings as s on w.[CompanyKeyValuePairSettingsId] = s.[CompanyKeyValuePairSettingsId] where s.Name Like #str + 'EndTime%')
INSERT INTO #TimeSlots (StartTime,EndTime) VALUES (#StartSlot,#EndSlot)
----
set #MinuteBeginDay = (select CONVERT(INT, (SELECT SUBSTRING((select top (1) StartTime from #TimeSlots), 1, 2))) * 60 + CONVERT(INT, (SELECT SUBSTRING((select top (1) StartTime from #TimeSlots), 4, 5))))
set #MinuteEndDay = (select CONVERT(INT, (SELECT SUBSTRING((select top (1) EndTime from #TimeSlots), 1, 2))) * 60 + CONVERT(INT, (SELECT SUBSTRING((select top (1) EndTime from #TimeSlots), 4, 5))))
set #MinuteAssignmentBegin = 30
While (#MinuteAssignmentBegin < #MinuteAssignmentEnd - #MinuteAssignmentBegin)
Begin
INSERT INTO [dbo].[tblEmployeeFreetime] ([StakeholderId], StartTime,EndTime,[AssignmentLength]) (select #EmployeeId, #current , DATEADD(minute, #MinuteBeginDay, #current),#MinuteAssignmentBegin)
if #StartSlot != #EndSlot
begin
set #SetTime = DATEADD(minute, #MinuteEndDay - #MinuteAssignmentBegin, #current)
INSERT INTO [dbo].[tblEmployeeFreetime] ([StakeholderId], StartTime,EndTime,[AssignmentLength]) (select #EmployeeId, #SetTime, DATEADD(minute, 1439, #current),#MinuteAssignmentBegin)
end
set #MinuteAssignmentBegin = #MinuteAssignmentBegin + 30
End
delete from #TimeSlots
Set #current = CONVERT (date, DATEADD(day, 1, #current))
End

Creating Dynamic Pivot Table Column Names

I have created a pivot table with hard coded column names. The pivot table simply keeps a rolling sum of sales by qty (current month + 11 months back).
It was my first time using the PIVOT function properly and the code works fine.
SELECT
Item_Code_Desc,
ISNULL([Current],0) AS [Current],
ISNULL([1],0) AS [1],
ISNULL([2],0) AS [2],
ISNULL([3],0) AS [3],
ISNULL([4],0) AS [4],
ISNULL([5],0) AS [5],
ISNULL([6],0) AS [6],
ISNULL([7],0) AS [7],
ISNULL([8],0) AS [8],
ISNULL([9],0) AS [9],
ISNULL([10],0) AS [10],
ISNULL([11],0) AS [11]
FROM
(SELECT
CONCAT(ST.Code,' - ', ST.Description_1) AS Item_Code_Desc,
STT.ActualQuantity AS Qty,
CASE
WHEN MONTH(STT.TxDate) = MONTH(GETDATE()) THEN 'Current'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -1, GETDATE())) THEN '1'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -2, GETDATE())) THEN '2'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -3, GETDATE())) THEN '3'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -4, GETDATE())) THEN '4'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -5, GETDATE())) THEN '5'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -6, GETDATE())) THEN '6'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -7, GETDATE())) THEN '7'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -8, GETDATE())) THEN '8'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -9, GETDATE())) THEN '9'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -10, GETDATE())) THEN '10'
WHEN MONTH(STT.TxDate) = MONTH(DATEADD(MONTH, -11, GETDATE())) THEN '11'
ELSE '0'
END AS [Period]
FROM
_bvSTTransactionsFull AS STT
INNER JOIN
StkItem AS ST ON STT.AccountLink = ST.StockLink
WHERE
STT.TxDate >= DATEADD(MONTH, -11, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
AND STT.Module = 'AR') AS P
PIVOT
(SUM(P.Qty)
FOR P.Period IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[Current])
) AS PVT
To make the output more dynamic, I wanted to rather show the month and year as the field heading, rather than just the 1,2,3 etc.
To attempt this, I first took out the original CASE, and used the CONCAT function to get the desired result
CONCAT(DATENAME(MONTH,STT.TxDate),' ',YEAR(STT.TxDate)) AS [Period],
Now that the sub-query was showing the transaction date in the format "mmmm yyyy", I wanted the table to pivot on this. I did start wondering how it was going to do this as I was reaching the end, as the column names are not hard coded anymore.
After trying
PIVOT (
SUM(P.Qty)
FOR CONCAT(DATENAME(MONTH,P.TxDate),' ',YEAR(P.TxDate))
) AS PVT
And many other variations I did do some research, and I see this process is slightly more complex than I thought.
I haven't used the STUFF and FOR XML PATH before. I have attempted to convert the above into the examples I have found on the net. But I'm trying in vein as I don't understand the core logic of what I am trying to do.
Please could I have assistance with not only how to get the pivot dynamic, but perhaps some notes to further understand what is happening. Really appreciate some help on this!
After Attempting
This is my attempt to get it right:
DECLARE
#Cols NVARCHAR(MAX),
#Query NVARCHAR(MAX),
#Module NVARCHAR = 'AR'
SELECT
#Cols = STUFF((SELECT DISTINCT ',' + 'CONCAT(DATENAME(MONTH, STT.TxDate),,YEAR(STT.TxDate))' + QUOTENAME(NAME)
FROM _bvSTTransactionsFull AS STT
WHERE STT.Module = 'AR'
FOR XML PATH (''), TYPE).VALUE('.','NVARCHAR(MAX)'),1,1,'')
SELECT
#Query = '
SELECT
Item_Code,
Item_Desc,
' +''''+ #Cols + '''''
FROM
(SELECT
ST.Code AS Item_Code,
ST.Description_1 AS Item_Desc,
STT.ActualQuantity AS Qty,
CONCAT(DATENAME(MONTH, STT.TxDate),,YEAR(STT.TxDate)) AS [Period]
FROM
_bvSTTransactionsFull AS STT
INNER JOIN
StkItem AS ST ON STT.AccountLink = ST.StockLink
WHERE
STT.TxDate >= DATEADD(MONTH, -13, DATEADD(MONTH, DATEDIFF(MONTH, 0,
GETDATE()), 0))
AND STT.Module = '+ #Module +') AS P
PIVOT
(SUM(P.Qty)
FOR P.Period IN ('+#cols+')
) AS PVT '
PRINT #Query
EXEC (#Query)
But I'm getting the following error:
Msg 207, Level 16, State 1, Line 7
Invalid column name 'NAME'.
Where am I off here?
Your result is correct above however, you will not have your columns sorted by the correct date...
The below would cater for that:
DECLARE
#Cols1 VARCHAR(MAX),
#Cols2 VARCHAR(MAX),
#Query VARCHAR(MAX),
#Period VARCHAR(MAX) = -13 ; --/ Select number of months to view back on (excluding current month) \--
declare #tmptbl table (PeriodDate datetime, col1 varchar(100), col2 varchar(100))
insert into #tmptbl (PeriodDate, col1, col2)
SELECT DISTINCT dPeriodDate,'ISNULL('+ QUOTENAME(CONCAT(DATENAME(MONTH, dPeriodDate),' ',YEAR(dPeriodDate)))+',0) AS' + QUOTENAME(CONCAT(DATENAME(MONTH, dPeriodDate),' ',YEAR(dPeriodDate))) col1
, QUOTENAME(CONCAT(DATENAME(MONTH, dPeriodDate),' ',YEAR(dPeriodDate))) col2
FROM _bvSTTransactionsFull AS S join _etblPeriod p on EOMONTH(s.TxDate) = p.dPeriodDate
WHERE S.Module = 'AR' AND S.TxDate > = DATEADD(MONTH, -13, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
SELECT
#Cols1 = STUFF((SELECT ',' + col1
FROM #tmptbl order by PeriodDate
FOR XML PATH (''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'') --/ allows the first SELECT fields to have the ISNULL function and Alias \--
SELECT
#Cols2 = STUFF((SELECT ',' + col2
FROM #tmptbl order by PeriodDate
FOR XML PATH (''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'') --/ becasue of the need for ISNULL above, second #Cols needed for the Pivot (pivot cannot have ISNULL in it) \--
SELECT
#Query =
'SELECT
Item_Code_Desc,
'+#Cols1+'
FROM
(SELECT
CONCAT(ST.Code,'' - '', ST.Description_1) AS Item_Code_Desc,
STT.ActualQuantity AS Qty,
CONCAT(DATENAME(MONTH, STT.TxDate),'' '',YEAR(STT.TxDate)) AS [Period]
FROM
_bvSTTransactionsFull AS STT
INNER JOIN
StkItem AS ST ON STT.AccountLink = ST.StockLink
WHERE
STT.TxDate >= DATEADD(MONTH, '+#Period+', DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
AND STT.Module = ''AR'') AS P
PIVOT
(SUM(P.Qty)
FOR P.Period IN ('+#cols2+')
) AS PVT '
PRINT #Query
EXEC (#Query)
I left the changes I made in small caps for you to see the changes...
Solved :)
DECLARE
#Cols1 VARCHAR(MAX),
#Cols2 VARCHAR(MAX),
#Query VARCHAR(MAX),
#Period VARCHAR(MAX) = -12 ; --/ Select number of months to view back on (excluding current month) \--
SELECT
#Cols1 = STUFF((SELECT DISTINCT ',' +'ISNULL('+ QUOTENAME(CONCAT(DATENAME(MONTH, S.TxDate),' ',YEAR(S.TxDate)))+',0) AS' + QUOTENAME(CONCAT(DATENAME(MONTH, S.TxDate),' ',YEAR(S.TxDate)))
FROM _bvSTTransactionsFull AS S
WHERE S.Module = 'AR' AND S.TxDate > = DATEADD(MONTH, -13, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
FOR XML PATH (''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'') --/ allows the first SELECT fields to have the ISNULL function and Alias \--
SELECT
#Cols2 = STUFF((SELECT DISTINCT ',' + QUOTENAME(CONCAT(DATENAME(MONTH, S.TxDate),' ',YEAR(S.TxDate)))
FROM _bvSTTransactionsFull AS S
WHERE S.Module = 'AR' AND S.TxDate > = DATEADD(MONTH, -13, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
FOR XML PATH (''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'') --/ becasue of the need for ISNULL above, second #Cols needed for the Pivot (pivot cannot have ISNULL in it) \--
SELECT
#Query =
'SELECT
Item_Code_Desc,
'+#Cols1+'
FROM
(SELECT
CONCAT(ST.Code,'' - '', ST.Description_1) AS Item_Code_Desc,
STT.ActualQuantity AS Qty,
CONCAT(DATENAME(MONTH, STT.TxDate),'' '',YEAR(STT.TxDate)) AS [Period]
FROM
_bvSTTransactionsFull AS STT
INNER JOIN
StkItem AS ST ON STT.AccountLink = ST.StockLink
WHERE
STT.TxDate >= DATEADD(MONTH, '+#Period+', DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0))
AND STT.Module = ''AR'') AS P
PIVOT
(SUM(P.Qty)
FOR P.Period IN ('+#cols2+')
) AS PVT '
PRINT #Query
EXEC (#Query)

Arithmetic overflow error converting numeric to data type while executing stored procedure

ALTER procedure [dbo].[performance]
#startdate nvarchar(100),
#enddate nvarchar(100)
as begin
set NOCOUNT on;
select l.LocName,
v.Vtype,
SUM(DATEDIFF(MI,t.Paydate,t.DelDate)) as TotalDiff,
[dbo].[testfunction](CONVERT(decimal(10,1), AVG(
CONVERT(NUMERIC(18,2), DATEDIFF(SS,t.Paydate,t.DelDate) ) ))) as Average
from
Transaction_tbl t
left join
VType_tbl v
on t.vtid=v.vtid
left join
Location_tbl l
on t.Locid=l.Locid
where
t.Locid in(select t1.Locid from Transaction_tbl t1) and
dtime between '' + #startdate +'' and ''+#enddate+'' and
Status =5
group by v.Vtype,l.LocName,l.Locid order by l.Locid
end
also i have one function like this:
ALTER FUNCTION [dbo].[testfunction] (#dec NUMERIC(18, 2)) RETURNS Varchar(50)
AS
BEGIN
DECLARE
#hour integer,
#Mns integer,
#second decimal(18,3)
DECLARE #Average Varchar(50)
select #hour=CONVERT(int,#dec/60/60)
SELECT #Mns = convert(int, (#dec / 60) - (#hour * 60 ));
select #second=#dec % 60;
SELECT #Average =
convert(varchar(9), convert(int, #hour)) + ':' +
right('00' + convert(varchar(2), convert(int, #Mns)), 2) + ':' +
right('00' + CONVERT(decimal(10,0), convert(varchar(6), #second)), 6)
RETURN #Average
END
am passing date like this:2013-01-01 and 2013-05-01
while executing this am getting error:Arithmetic overflow error converting numeric to data type varchar.
Try this one -
ALTER PROCEDURE [dbo].[performance]
#startdate NVARCHAR(100), --<-- try to use date datatype - DATE, DATETIME, ...
#enddate NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
SELECT
l.LocName
, v.Vtype
, SUM(DATEDIFF(MI, t.Paydate, t.DelDate)) AS TotalDiff
, [dbo].[testfunction](
CONVERT(DECIMAL(10, 1), AVG(CONVERT(NUMERIC(18, 2), DATEDIFF(SS, t.Paydate, t.DelDate))))
) AS Average
FROM dbo.Transaction_tbl t
LEFT JOIN dbo.VType_tbl v ON t.vtid = v.vtid
LEFT JOIN dbo.Location_tbl l ON t.Locid = l.Locid
WHERE dtime BETWEEN
CAST(#startdate AS DATETIME)
AND
CAST(#enddate AS DATETIME) --<-- possible problem
AND [status] = 5
--AND t.Locid IN (SELECT t1.Locid FROM Transaction_tbl t1) --<-- unnessesary
GROUP BY
v.Vtype
, l.LocName
, l.Locid
ORDER BY l.Locid
END
Update:
ALTER FUNCTION [dbo].[testfunction]
(
#dec NUMERIC(18, 2)
)
RETURNS Varchar(50)
AS BEGIN
DECLARE
#hour BIGINT
, #Mns BIGINT
, #second DECIMAL(18,3)
SELECT
#hour = CONVERT(BIGINT, #dec / 60 / 60)
, #Mns = CONVERT(BIGINT, (#dec / 60) - (#hour * 60))
, #second = #dec % 60
RETURN
CONVERT(VARCHAR(50), CONVERT(BIGINT, #hour)) + ':' + --<-- VARCHAR(9) => VARCHAR(50)
RIGHT('00' + CONVERT(VARCHAR(2), CONVERT(BIGINT, #Mns)), 2) + ':' +
RIGHT('00' + CONVERT(DECIMAL(2, 0), CONVERT(VARCHAR(6), #second)), 6)
END

Dynamic SQL value from table

I'm working with a dynamic query right now and I need values from a table.
My query so far:
DECLARE #query varchar(1500)
SET #query =
'SELECT Id,'
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(myTable.myDate AS date),120)+'] = ['+convert(varchar(50), CAST(GETDATE() AS date), 120)+'] then [Counter] else 0 end) ''0'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -1, myTable.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -1, GETDATE()) AS date), 120)+'] then [counter] else 0 end) ''1'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -2, myTable.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -2, GETDATE()) AS date), 120)+'] then [counter] else 0 end) ''2'','
+ 'SUM(CASE WHEN ['+convert(varchar(50), CAST(DATEADD(day, -3, myTable.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -3, GETDATE()) AS date),120)+'] then [counter] else 0 end) ''3'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -4, myTable.myDate)),120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -4, GETDATE()) AS date),120)+'] then [counter] else 0 end) ''4'''
+ 'FROM [myTable] GROUP BY Id'
PRINT(#query)
EXEC(#query)
This wont work because it says "Multi-part identifyer "myTable" could not be bound." on rows 4-8.
Ive seen people recomending something like this:
CREATE TABLE #tmp
(
id varchar(50),
counter int,
myDate smalldatetime
)
Insert Into #tmp (id, counter, myDate)
SELECT * FROM myTable
--GO --doesn't work either with or without 'go'
DECLARE #query varchar(1500)
SET #query =
'SELECT Id,'
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(#tmp.myDate AS date),120)+'] = ['+convert(varchar(50), CAST(GETDATE() AS date), 120)+'] then [counter] else 0 end) ''0'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -1, #tmp.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -1, GETDATE()) AS date), 120)+'] then [counter] else 0 end) ''1'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -2, #tmp.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -2, GETDATE()) AS date), 120)+'] then [counter] else 0 end) ''2'','
+ 'SUM(CASE WHEN ['+convert(varchar(50),CAST(DATEADD(day, -3, #tmp.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -3, GETDATE()) AS date),120)+'] then [counter] else 0 end) ''3'','
+ 'SUM(CASE WHEN [+'convert(varchar(50),CAST(DATEADD(day, -4, #tmp.myDate)), 120)+'] = ['+convert(varchar(50),CAST(DATEADD(day, -4, GETDATE()) AS date),120)+'] then [aounter] else 0 end) ''4'''
+ 'FROM [myTable] GROUP BY Id'
PRINT(#query)
EXEC(#query)
Still doesn't work. Please help!
SIMPLIFIED VERSION OF MY PROBLEM:
DECLARE #query varchar(1500)
SET #query = 'SELECT ['+myTable.value+'] FROM [myTable]'
EXEC(#query)
doesn't work
Try something like this:
CREATE TABLE #tmp
(
id varchar(50),
counter int,
myDate smalldatetime
)
Insert Into #tmp (id, counter, myDate)
SELECT * FROM myTable
DECLARE #query varchar(1500)
Select #query =
'SELECT Id,'
+ 'SUM(CASE WHEN ''' + convert(varchar(50), #tmp.myDate,120) + ''' = '''+convert(varchar(50), GETDATE(), 120)+''' then ''counter'' else 0 end) ''0'','
+ 'SUM(CASE WHEN '''+convert(varchar(50), DATEADD(day, -1, #tmp.myDate), 120)+''' = '''+convert(varchar(50),DATEADD(day, -1, GETDATE()), 120)+''' then ''counter'' else 0 end) ''1'','
+ 'SUM(CASE WHEN '''+convert(varchar(50), DATEADD(day, -2, #tmp.myDate), 120)+''' = '''+convert(varchar(50),DATEADD(day, -2, GETDATE()), 120)+''' then ''counter'' else 0 end) ''2'','
+ 'SUM(CASE WHEN '''+convert(varchar(50), DATEADD(day, -3, #tmp.myDate), 120)+''' = '''+convert(varchar(50),DATEADD(day, -3, GETDATE()), 120)+''' then ''counter'' else 0 end) ''3'','
+ 'SUM(CASE WHEN '''+convert(varchar(50), DATEADD(day, -4, #tmp.myDate), 120)+''' = '''+convert(varchar(50),DATEADD(day, -4, GETDATE()), 120)+''' then ''counter'' else 0 end) ''4'''
+ ' FROM ' from #tmp
Set #query = #query + ' #tmp GROUP BY Id'
PRINT(#query)
EXEC(#query)

Help with SQL to get Hits per day for today and 1 month prior

I have this SQL now:
CREATE PROCEDURE dbo.sel_Track_HitsLast30Days(
#projectID int
)
AS
BEGIN
DECLARE #FirstDay smalldatetime, #NumberOfMonths int, #priorMonth smalldatetime
set #priorMonth = (SELECT CAST(
(
STR( YEAR( dateadd(m,-1, getDate()) ) ) + '/' +
STR( MONTH( dateadd(m,-1, getDate()) ) ) + '/' +
STR( DAY( dateadd(m,-1, getDate()) ) )
)
AS DateTime
))
Select #FirstDay = #priorMonth, #NumberOfMonths = 1
;WITH Days AS (
SELECT #FirstDay as CalendarDay
UNION ALL
SELECT DATEADD(d, 1, CalendarDay) as CalendarDay
FROM Days
WHERE DATEADD(d, 1, CalendarDay) < DATEADD(m, #NumberOfMonths, #FirstDay+1)
)
SELECT calendarday,foundDate.TotalbyDate,foundDate.date FROM Days
LEFT OUTER JOIN (
SELECT
COUNT(LEFT(visitDateTime, 11)) AS TotalbyDate,substring(convert( char(10), CONVERT( char(10), visitDateTime, 121 ) ), 1, 11) AS date
FROM
dbo.TrackingData
WHERE
visitDateTime >= dateadd(d, datediff(d, 0, getdate()), -30) and projectID = #projectID
GROUP BY substring(convert( char(10), CONVERT( char(10), visitDateTime, 121 ) ), 1, 11)
) foundDate on foundDate.date = CalendarDay
order by
CalendarDay Desc
END
This works ok, but It is not taking into account months with 31 days and I am not getting back today's date for some reason.
Try this (using AdventureWorks as sample database)
DECLARE #today datetime, #NumberOfMonths int, #FirstDay smalldatetime
SELECT #today = '2004-03-09', -- getdate(), tests only
#NumberOfMonths = 1,
#FirstDay = CAST(FLOOR(CAST(
DATEADD(M, -1, #today) AS float)) AS datetime);
WITH Days AS
(
SELECT #FirstDay AS CalendarDay UNION ALL
SELECT DATEADD(d, 1, CalendarDay) AS CalendarDay FROM Days
WHERE DATEADD(d, 1, CalendarDay) < DATEADD(m, #NumberOfMonths, #FirstDay+1)
)
SELECT CONVERT(varchar(10), CalendarDay, 111) as [Date],
COUNT(TransactionDate) as [Count]
FROM Days LEFT JOIN Production.TransactionHistory
ON TransactionDate = Days.CalendarDay
GROUP BY CalendarDay
ORDER BY CalendarDay
Will output
Date Count
---------- -----------
2004/02/09 272
2004/02/10 308
2004/02/11 264
2004/02/12 265
2004/02/13 250
...
EDIT: Updated to include all interval dates
HEre is a modified version of Ruben's answer:
DECLARE #today DATETIME,
#firstDayLastMonth DATETIME,
#daysCount int
SET #today = getDate()
SET #firstDayLastMonth = Dateadd(m,-1,Dateadd(d,-Day(#today) + 1,#today))
Set #daysCount = (select datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#firstDayLastMonth) as varchar)+'-'+cast(month(#firstDayLastMonth) as varchar)+'-01' as datetime)))))
SELECT substring(convert( char(10), CONVERT( char(10), visitDateTime, 121 ) ), 1, 11) AS [Date], Count(* ) AS [Count]
FROM dbo.TrackingData
WHERE visitdatetime >= Dateadd(d,-#daysCount,#today)
GROUP BY substring(convert( char(10), CONVERT( char(10), visitDateTime, 121 ) ), 1, 11)
ORDER BY substring(convert( char(10), CONVERT( char(10), visitDateTime, 121 ) ), 1, 11)
OK, I got it. Rubens gave me an idea so I modified my SQL like so:
DECLARE #FirstDay SMALLDATETIME,
#NumberOfMonths INT,
#priorMonth SMALLDATETIME,
#firstDayLastMonth DateTime,
#daysCount int
set #firstDayLastMonth = dateadd(m, -1, dateadd(d, -day(getDate()) + 1, getDate()))
set #daysCount = (select datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#firstDayLastMonth) as varchar)+'-'+cast(month(#firstDayLastMonth) as varchar)+'-01' as datetime)))))
SET #priorMonth = (SELECT Cast((Str(Year(Dateadd(m,-1,Getdate()))) + '/' + Str(Month(Dateadd(m,-1,Getdate()))) + '/' + Str(Day(Dateadd(m,-1,Getdate())))) AS DATETIME))
Declare #before dateTime
Set #before = Dateadd(d,-#daysCount,getdate())
SELECT #FirstDay = #before,
#NumberOfMonths = 1;
WITH days
AS (SELECT #FirstDay AS calendarday
UNION ALL
SELECT Dateadd(d,1,calendarday) AS calendarday
FROM days
WHERE Dateadd(d,1,calendarday) <= Dateadd(m,#NumberOfMonths,#FirstDay))
SELECT Substring(Convert(CHAR(10),Convert(CHAR(10),calendarday,101)),
1,11) ,
founddate.totalbydate,
founddate.DATE
FROM days
LEFT OUTER JOIN (SELECT Count(Left(visitdatetime,11)) AS totalbydate,
Substring(Convert(CHAR(10),Convert(CHAR(10),visitdatetime,101)),
1,11) AS DATE
FROM dbo.trackingdata
WHERE visitdatetime >= Dateadd(d,Datediff(d,0,Getdate()),-29)
AND projectid = 131
GROUP BY Substring(Convert(CHAR(10),Convert(CHAR(10),visitdatetime,101)),
1,11)) founddate
ON founddate.DATE = Substring(Convert(CHAR(10),Convert(CHAR(10),calendarday,101)),
1,11)

Resources