Related
I am working on an sql query where i will give a date from which Month and year will be determined. Then the record will be selected from Lab_Analysis and Lab_CSAnalysis table of any Furnace and Product ID. For this i Did following
Declare #Furnace varchar(50)='FUR-A'
Declare #Product bigint=1
Declare #sd date
Declare #ed date
Declare #Date date='02-02-2019'
SET #sd=(SELECT DATEADD(s,1,DATEADD(mm, DATEDIFF(m,0,#Date),0)))
SET #ed=(SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#Date)+1,0)))
;WITH dates AS (
SELECT #sd as theDate
UNION ALL
SELECT DATEADD(day, 1, theDate)
FROM dates
WHERE DATEADD(day, 1, theDate) <= #ed
)
SELECT DATEPART(dd, D.theDate) AS 'Day', ISNULL(LS.QtyMT, 0) AS QtyMt, ISNULL(SUM(L.Mn), 0) AS Mn, ISNULL(SUM(L.Si), 0) AS Si, ISNULL(SUM(L.P), 0) AS P,
ISNULL(LS.Carbon, 0) AS C, ISNULL(LS.Sulphur, 0) AS S, ISNULL(SUM(L.MnO), 0) AS MnO, ISNULL(SUM(L.CaO), 0) AS CaO, ISNULL(SUM(L.AI2O3), 0)
AS AI2O3, ISNULL(SUM(L.MgO), 0) AS MgO, ISNULL(SUM(L.Fe2O3), 0) AS Fe2O3, ISNULL(SUM(L.SiO2), 0) AS SiO2, ISNULL(SUM(L.Basicity), 0) AS Basicity
FROM Lab_Product AS LP INNER JOIN
Lab_Analysis AS L ON LP.ID = L.Product RIGHT OUTER JOIN
dates AS D ON L.Date = theDate LEFT OUTER JOIN
Lab_CSAnalysis AS LS ON LS.Date = L.Date AND (LP.ID = 1 AND L.Furnace = #Furnace AND LS.Furnace=#Furnace)
GROUP BY D.theDate, LS.QtyMT, LS.Carbon, LS.Sulphur
Here i want to view Record of Furnace='A' and ProductID=1. Then the above query is correct Output. There is no Record in Other than Furnace 'A' and Product ID=1. But when i want to View records of Furnace 'B' then it is still showing records of Furnace 'A'. How to solve this?
because of your filter on the left join. I made some changing over your query
Declare #Furnace varchar(50)='FUR-A'
Declare #Product bigint=1
Declare #sd date
Declare #ed date
Declare #Date date='02-02-2019'
SET #sd=(SELECT DATEADD(s,1,DATEADD(mm, DATEDIFF(m,0,#Date),0)))
SET #ed=(SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#Date)+1,0)))
;WITH dates AS (
SELECT #sd as theDate
UNION ALL
SELECT DATEADD(day, 1, theDate)
FROM dates
WHERE DATEADD(day, 1, theDate) <= #ed
)
SELECT DATEPART(dd, D.theDate) AS 'Day',
ISNULL(LS.QtyMT, 0) AS QtyMt,
ISNULL(SUM(L.Mn), 0) AS Mn,
ISNULL(SUM(L.Si), 0) AS Si,
ISNULL(SUM(L.P), 0) AS P,
ISNULL(LS.Carbon, 0) AS C,
ISNULL(LS.Sulphur, 0) AS S,
ISNULL(SUM(L.MnO), 0) AS MnO,
ISNULL(SUM(L.CaO), 0) AS CaO,
ISNULL(SUM(L.AI2O3), 0) AS AI2O3,
ISNULL(SUM(L.MgO), 0) AS MgO,
ISNULL(SUM(L.Fe2O3), 0) AS Fe2O3,
ISNULL(SUM(L.SiO2), 0) AS SiO2,
ISNULL(SUM(L.Basicity), 0) AS Basicity
FROM
dates
INNER JOIN Lab_Analysis AS L ON L.Date = dates.theDate AND L.Furnace = #Furnace
LEFT JOIN Lab_Product AS LP ON LP.ID = L.Product AND LP.ID = 1
LEFT JOIN Lab_CSAnalysis AS LS ON LS.Date = L.Date AND (L.Furnace = LS.Furnace)
GROUP BY
D.theDate, LS.QtyMT, LS.Carbon, LS.Sulphur
I created a "Daily Sales Query" that captures all the Total Sales entered from the previous work day which runs Mon-Fri at 8AM.
Question is, if it is Monday today, how can I capture the records from Friday.
so that I can exclude weekends.
Because if it is Monday, the total sales displays 0 which actually makes sense because Sunday is a not a work day. Please assist.
See my current code:
SELECT
CONVERT(VARCHAR, DATEADD(dd, - 1, GETDATE()), 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
(o.EntryDate >= CONVERT(CHAR(8), DATEADD(dd, - 1, GETDATE()), 112))
AND (o.EntryDate < CONVERT(CHAR(8), GETDATE(), 112))
AND (o.CustomerID <> 187);
You can use datediff(dd,0,getdate()) % 7 = 0 to determine if the current date is a Monday regardless of any other server settings (this is because the zero date in SQL Server is 1900-01-01 which happens to be a Monday).
declare #start date;
declare #finish date;
set #start = dateadd(dd, case when datediff(dd,0,getdate()) % 7 = 0 then -3 else -1 end, getdate());
set #finish = dateadd(dd,1,#start);
select
#start, datename(weekday,#start)
, #finish, datename(weekday,#finish)
, datename(weekday,getdate())
;
So in your query I would use:
declare #start date;
declare #finish date;
set #start = dateadd(dd, case when datediff(dd,0,getdate()) % 7 = 0 then -3 else -1 end, getdate());
set #finish = dateadd(dd,1,#start);
SELECT
CONVERT(VARCHAR, #start, 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
o.EntryDate >= #start
AND o.EntryDate < #finish
AND o.CustomerID <> 187
;
If you use a case statement to determine how many days in the past you need to go e.g.
dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()) -- StartDate
dateadd(dd, case when datepart(weekday,getdate()) = 1 then -2 else 0 end, getdate()) -- EndDate
so your code would look like
SELECT
CONVERT(VARCHAR, dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()), 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
(o.EntryDate >= CONVERT(CHAR(8), dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()), 112))
AND (o.EntryDate < CONVERT(CHAR(8), dateadd(dd, case when datepart(weekday,getdate()) = 1 then -2 else 0 end, getdate()), 112))
AND (o.CustomerID <> 187);
PS: Do confirm that weekday 1 is Monday on your server.
I am using CTE for union two columns in sql.
If I am executing these two queries it is working fine .
But I want to make union of these two for more simpler O/P.
I am getting compile time error :
Incorrect syntax near the keyword 'DECLARE'.
can anyone tell me hoe to achieve union with cte
DECLARE #Inward DATETIME
SET #Inward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
WITH cte AS
(
SELECT 0 AS TheMonth
UNION ALL
SELECT TheMonth + 1
FROM cte
WHERE TheMonth < 5
)
SELECT
cte.TheMonth,
isnull(sum(qty),0) as inward
FROM
cte
LEFT OUTER JOIN RS_GIN_Master as g
ON accept_date >= DATEADD(MM, cte.TheMonth, #Inward) AND accept_date < DATEADD(MM, cte.TheMonth + 1, #Inward)
UNION all
DECLARE #Outward DATETIME
SET #Outward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
WITH cte AS
(
SELECT 0 AS TheMonthO
UNION ALL
SELECT TheMonthO + 1
FROM cte
WHERE TheMonthO < 5
)
SELECT isnull(sum(quantity),0) as outward
FROM
cte
LEFT OUTER JOIN
RS_Sell_Order_Master as s
ON del_date >= DATEADD(MM, cte.TheMonthO, #Outward) AND del_date < DATEADD(MM, cte.TheMonthO + 1, #Outward) and isDelivered = 1
left outer join RS_Sell_Order_Mapping as sm on sm.sell_order_no = s.sell_order_no
Where to begin.
You cannot use declare statements inside of union statements.
Union statements must return the same number of columns.
Common table expressions should only be defined at the beginning of your statement.
Your Sum aggregate needs a group by to return each month.
Perhaps you're looking for something like this:
DECLARE #Inward DATETIME
DECLARE #Outward DATETIME
SET #Inward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
SET #Outward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
WITH cte AS
(
SELECT 0 AS TheMonth
UNION ALL
SELECT TheMonth + 1
FROM cte
WHERE TheMonth < 5
)
SELECT
cte.TheMonth,
isnull(sum(qty),0) as inward,
null as outward
FROM
cte
LEFT OUTER JOIN RS_GIN_Master as g
ON accept_date >= DATEADD(MM, cte.TheMonth, #Inward)
AND accept_date < DATEADD(MM, cte.TheMonth + 1, #Inward)
GROUP BY cte.TheMonth
UNION all
SELECT
cte.TheMonth,
null as inward,
isnull(sum(quantity),0) as outward
FROM
cte
LEFT OUTER JOIN RS_Sell_Order_Master as s
ON del_date >= DATEADD(MM, cte.TheMonthO, #Outward)
AND del_date < DATEADD(MM, cte.TheMonthO + 1, #Outward) and isDelivered = 1
left outer join RS_Sell_Order_Mapping as sm on
sm.sell_order_no = s.sell_order_no
GROUP BY cte.TheMonth
Ha! I'm looked it today because i had the same question. You must declare the first CTE with the "WITH" Keyword and every CTE you need, just separate it with a comma.
EDIT from sgeedes Query above
DECLARE #Inward DATETIME
DECLARE #Outward DATETIME
SET #Inward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
SET #Outward = DATEADD(mm, -6, CURRENT_TIMESTAMP);
WITH cte AS
(
SELECT 0 AS TheMonth
UNION ALL
SELECT TheMonth + 1
FROM cte
WHERE TheMonth < 5
)
SELECT TheMonth,sum(Inward) as InWard, sum(OutWard) as OutWard
FROM
(
SELECT
cte.TheMonth,
isnull(sum(qty),0) as inward,
0 as outward
FROM
cte
LEFT OUTER JOIN RS_GIN_Master as g
ON accept_date >= DATEADD(MM, cte.TheMonth, #Inward)
AND accept_date < DATEADD(MM, cte.TheMonth + 1, #Inward)
GROUP BY cte.TheMonth
UNION all
SELECT
cte.TheMonth,
0 as inward,
isnull(sum(quantity),0) as outward
FROM
cte
LEFT OUTER JOIN RS_Sell_Order_Master as s
ON del_date >= DATEADD(MM, cte.TheMonthO, #Outward)
AND del_date < DATEADD(MM, cte.TheMonthO + 1, #Outward) and isDelivered = 1
left outer join RS_Sell_Order_Mapping as sm on
sm.sell_order_no = s.sell_order_no
GROUP BY cte.TheMonth
)Z
I have a query that will return the total number of calls I received each day of last month for a queue. if no calls it will return zero for that day. However when running this report for for December of 2013 the results are blank. It appears to be due to the fact the query is returning the dates with the year of 2014 instead of 2013. How can I adjust the query so the date is 12/01/2013, etc.
DECLARE #pMnth int,#pYr int,#pQueue varchar
SET #pMnth = '12'
SET #pYr = '2013'
SET #pQueue = 'Queue Name'
;
WITH
CTE_Days AS
(
SELECT DATEADD(month, #pMnth, DATEADD(month, -MONTH(GETDATE()), DATEADD(day, -DAY(GETDATE()) + 1, CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME)))) as Dt
UNION ALL
SELECT DATEADD(day, 1, Dt)
FROM CTE_Days
WHERE Dt < DATEADD(day, -1, DATEADD(month, 1, DATEADD(month, #pMnth, DATEADD(month, -MONTH(GETDATE()), DATEADD(day, -DAY(GETDATE()) + 1, CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME))))))
)
SELECT tbl1.Dt,ISNULL(Calls,0) AS Calls
FROM CTE_Days tbl1
LEFT JOIN(
SELECT CAST(clmdate AS Date) AS ClDt,COUNT(*) AS Calls
FROM dbo.tblcalls
WHERE DATEPART(yyyy,clmdate) = #pYr
AND DATEPART(mm,clmdate) = #pMnth
AND clmqueue = #pQueue
GROUP BY CAST(clmdate AS Date)
) tbl2 ON tbl1.Dt = tbl2.ClDt
Not really sure what you're trying to do with all the variables, but if you want the first day of last month:
SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE())-1, 0) -- < SQL 2012
SELECT DATEADD(day,1,EOMONTH(GETDATE(),-2)) -- SQL 2012
Last day of last month:
SELECT DATEADD(day,-1,DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)) -- < SQL 2012
SELECT EOMONTH(GETDATE(),-1) -- SQL 2012
In your code (assuming you're not on 2012):
DECLARE #pMnth int,#pYr int,#pQueue varchar
SET #pMnth = '12'
SET #pYr = '2013'
SET #pQueue = 'Queue Name'
;
WITH
CTE_Days AS
(
SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE())-1, 0) as Dt
UNION ALL
SELECT DATEADD(day, 1, Dt)
FROM CTE_Days
WHERE Dt < DATEADD(day,-1,DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))
)
SELECT tbl1.Dt,ISNULL(Calls,0) AS Calls
FROM CTE_Days tbl1
LEFT JOIN(
SELECT CAST(clmdate AS Date) AS ClDt,COUNT(*) AS Calls
FROM dbo.tblcalls
WHERE DATEPART(yyyy,clmdate) = #pYr
AND DATEPART(mm,clmdate) = #pMnth
AND clmqueue = #pQueue
GROUP BY CAST(clmdate AS Date)
) tbl2 ON tbl1.Dt = tbl2.ClDt
Update:
To incorporate your variables, I would change the month and year variables to strings, and replace GETDATE() with a date variable:
DECLARE #pMnth CHAR(2)
,#pYr CHAR(4)
,#pQueue VARCHAR(MAX)
,#dt DATE
SET #pMnth = '12'
SET #pYr = '2013'
SET #pQueue = 'Queue Name'
SET #dt = CAST(#pYr+RIGHT('0'+#pMnth,2)+'01' AS DATE)
WITH
CTE_Days AS
(
SELECT DATEADD(month, DATEDIFF(month, 0, #dt)-1, 0) as Dt
UNION ALL
SELECT DATEADD(day, 1, Dt)
FROM CTE_Days
WHERE Dt < DATEADD(day,-1,DATEADD(month, DATEDIFF(month, 0, #dt), 0))
)
SELECT tbl1.Dt,ISNULL(Calls,0) AS Calls
FROM CTE_Days tbl1
LEFT JOIN(
SELECT CAST(clmdate AS Date) AS ClDt,COUNT(*) AS Calls
FROM dbo.tblcalls
WHERE DATEPART(yyyy,clmdate) = #pYr
AND DATEPART(mm,clmdate) = #pMnth
AND clmqueue = #pQueue
GROUP BY CAST(clmdate AS Date)
) tbl2 ON tbl1.Dt = tbl2.ClDt
I am currently working on a function in which I use a recursive CTE, but it seems that have poor performance. I need this to be in function (so no temp tables) so I can easily use it within stored procedures.
Here is the code:
CREATE FUNCTION [dbo].[Web_GetDailyLoadListUDF]
(
#CustomerID INT
, #StartDate DATETIME
, #Days INT
, #IncludeChildren BIT
)
RETURNS #TableOfValues TABLE
(
RowID SMALLINT IDENTITY(1,1)
, DailyLoadCount INT
, DailyLoadDate VARCHAR(6)
, FullDate DATETIME
)
AS
BEGIN
DECLARE #MaxDate DATETIME;
SET #MaxDate = DATEADD(dd, #Days * -1.7, DATEDIFF(dd, 0, #StartDate));
WITH DateCTE AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)) AS DateValue
UNION ALL
SELECT DATEADD(DAY, -1, DateValue)
FROM DateCTE
WHERE DATEADD(DAY, -1, DateValue) > #MaxDate
)
INSERT INTO #TableOfValues
SELECT * FROM
(
SELECT TOP (#Days)
(
SELECT COUNT(*)
FROM dbo.[Load] l WITH (NOLOCK)
JOIN dbo.LoadCustomer lc WITH (NOLOCK)
ON lc.LoadID = l.ID
JOIN dbo.Customer c WITH (NOLOCK)
ON c.ID = lc.CustomerID
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
AND l.StateType = 1
AND lc.Main = 1
AND (c.ID = #CustomerID OR (#IncludeChildren = 1 AND c.ParentCustomerID = #CustomerID))
) AS DailyLoadCount
, CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
, dct.DateValue
FROM DateCTE dct
WHERE
DATEPART(DW, dct.DateValue) NOT IN (1, 7)
AND dct.DateValue NOT IN
(
SELECT HolidayDate FROM Holiday
)
ORDER BY dct.DateValue DESC
) AS S
ORDER BY s.DateValue ASC
RETURN
END
What this SQL is supposed to retrieve is the number of loads per day, for the past #Days that are business days (no weekends/holidays).
I basically just need some help optimizing this so that it doesn't run so slow. (Takes up to 20 seconds per customer, and this will be called over thousands).
Your main problem is just here
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
It should be
WHERE l.LoadDate >= dct.DateValue
AND l.LoadDate < dct.DateValue +1
Create composite indexs on Load(LoadDate, ID) and Load(ID, LoadDate) and drop the one that does not get used in the query plan.
You should show the query plan whenever you are asking questions about performance. To view the query plan, run the query inside the function on its own using variables for the input parameters. From the menu in SSMS, enable the option "Query -> Include Actual Execution Plan"
Since you don't have enough rep to post images, you can reveal the text plan as follows. Provide some sensible parameters in the first SELECT statement.
set showplan_text on;
Then, run the below in TEXT mode, i.e. press Ctrl-T then Ctrl-E.
DECLARE
#CustomerID INT
, #StartDate DATETIME
, #Days INT
, #IncludeChildren BIT
SELECT
#CustomerID = 1
, #StartDate = '20110201'
, #Days = 10
, #IncludeChildren = 1
DECLARE #TableOfValues TABLE
(
RowID SMALLINT IDENTITY(1,1)
, DailyLoadCount INT
, DailyLoadDate VARCHAR(6)
, FullDate DATETIME
)
DECLARE #MaxDate DATETIME;
SET #MaxDate = DATEADD(dd, #Days * -1.7, DATEDIFF(dd, 0, #StartDate));
WITH DateCTE AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)) AS DateValue
UNION ALL
SELECT DATEADD(DAY, -1, DateValue)
FROM DateCTE
WHERE DATEADD(DAY, -1, DateValue) > #MaxDate
)
INSERT INTO #TableOfValues
SELECT * FROM
(
SELECT TOP (#Days)
(
SELECT COUNT(*)
FROM dbo.[Load] l WITH (NOLOCK)
JOIN dbo.LoadCustomer lc WITH (NOLOCK)
ON lc.LoadID = l.ID
JOIN dbo.Customer c WITH (NOLOCK)
ON c.ID = lc.CustomerID
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
AND l.StateType = 1
AND lc.Main = 1
AND (c.ID = #CustomerID OR (#IncludeChildren = 1 AND c.ParentCustomerID = #CustomerID))
) AS DailyLoadCount
, CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
, dct.DateValue
FROM DateCTE dct
WHERE
DATEPART(DW, dct.DateValue) NOT IN (1, 7)
AND dct.DateValue NOT IN
(
SELECT HolidayDate FROM Holiday
)
ORDER BY dct.DateValue DESC
) AS S
ORDER BY s.DateValue ASC
SELECT * FROM #TableOfValues
Edit the plan into your question
You should use an inline UDF instead (right now you are actually using a temp table)
See http://msdn.microsoft.com/en-us/library/ms189294.aspx
Or convert it into a view instead.
Correlated subqueries run row-by-row, do not use them. Use a join or a join to a derived table instead. You also need to make sure any where clauses can take advantage of the indexing. Search on saragble queries to see what kinds of things cannot use indexes and what can be done to make it use an index.