SQL query taking very long to execute - sql-server

I have two versions of the same query which I'm trying to execute on SQL Server 2008.
Version 1:
select
count(a.ordernumber) as Orders,
sum(b.agentfees) as AgentFees,
sum(a.revenue) as Revenue,
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date) as Date
from
orders a
join
[DC-SQL-V2].FaxFile.dbo.[agent fees] b on a.ordernumber = b.fforder
where
a.closeoutdate >= '2014-01-01'
and b.dtcreated >= '2014-01-01'
and a.closeoutdate < '2015-01-01'
and b.dtcreated < '2015-01-01'
and a.processserving = 1
and a.branchno = '116'
group by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)
order by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)
Version 1 takes around 3 hours to finish which is obviously insane. So I created Version 2 in which I tried to filter the tables first before joining them, but this still takes pretty long (2 hours and still going).
Version 2:
select
count(a.ordernumber) as Orders,
sum(b.agentfees) as AgentFees,
sum(a.revenue) as Revenue,
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date) as Date
from
(select
ordernumber, revenue, closeoutdate, processserving, branchno
from
ORDERS
where
closeoutdate >= '2014-01-01'
and closeoutdate < '2015-01-01'
and branchno = '116'
and processserving = 1) a
join
(select
agentfees, fforder, dtcreated
from
[DC-SQL-V2].FaxFile.dbo.[agent fees]
where
dtcreated >= '2014-01-01'
and dtcreated < '2015-01-01') b on a.ordernumber = b.fforder
group by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)
order by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)
Here are some additional details:
select count(*) from [DC-SQL-V2].FaxFile.dbo.[agent fees]
779531
select count(*) from ORDERS
3466648
Here are the clustered columns for both tables (among others that I'm not selecting):
ORDERS:
BranchNo
Closeoutdate
ProcessServing
AGENT FEES:
Nothing I'm selecting is clustered
Would someone please be able to suggest how I can improve the performance of my queries?

Assuming [DC-SQL-V2] is a remote, linked server, I'd recommend bringing that data local first.
/* Making broad assumptions here. Substitute correct data types */
CREATE TABLE #tmpAgentFees (
agentfees money,
fforder int,
dtcreated datetime
);
INSERT INTO #tmpAgentFees
(agentfees, fforder, dtcreated)
SELECT agentfees, fforder, dtcreated
FROM [DC-SQL-V2].FaxFile.dbo.[agent fees]
WHERE dtcreated >= '2014-01-01'
AND dtcreated < '2015-01-01';
/* Optionally, add an index to perhaps help performance of the join */
CREATE INDEX IX_tmpAgentFees_fforder
ON #tmpAgentFees(fforder)
INCLUDE (agentfees, dtcreated);
select count(a.ordernumber) as Orders, sum(b.agentfees) as AgentFees, sum(a.revenue) as Revenue,
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date) as Date
from orders a
join #tmpAgentFees b
on a.ordernumber = b.fforder
where a.closeoutdate >= '2014-01-01'
and a.closeoutdate < '2015-01-01'
and a.processserving=1
and a.branchno='116'
group by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)
order by
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date)

There are some general hints
use SQL Server Profiler to find the most costly part
use an index on join column fforder
CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date) is repeated again and again, if it is possible create a computer column in your table and store the value and create an index on this new column with include in index option on
if 3 is not possible use drive table for example as below
select * from
(
select col1,col2,..,CAST('1/' + CAST(DATEPART(Month, a.closeoutdate) as varchar) + '/' + CAST(DATEPART(Year, a.closeoutdate) as varchar) as Date) from table1 ) drivedTable
join .....

The costly part is where you are getting detail data from the foreign server and then summarizing it. You should summarize it their and then only bring that back. It is not clear to me how "dtcreated" and "closeoutdate" are related?
Below I summarized the data by month and then brought it over to the local server. So only 12 rows are coming over instead of one per order. This may not be what you want if the fee should be counted in the "closeoutdate" month. But your code had a problem where it would never count an order or fee if there dates were in different years. (Need to clarify what is needed with that.)
select
a.CloseMonth,
a.Orders,
b.AgentFees
a.Revenue
from
(select year(closeoutdate) * 100 + month(closeoutdate) as CloseMonth,
count(a.ordernumber) as Orders,
sum(a.revenue) as Revenue
from orders
where closeoutdate >= '2014-01-01'
and closeoutdate < '2015-01-01'
and branchno = '116'
and processserving = 1
group by year(closeoutdate) * 100 + month(closeoutdate)) as a
join
(select year(dtcreated) * 100 + month(dtcreated) as FeeMonth,
sum(agentfees) AgentFees
from [DC-SQL-V2].FaxFile.dbo.[agent fees]
where
dtcreated >= '2014-01-01'
and dtcreated < '2015-01-01'
group by year(dtcreated) * 100 + month(dtcreated)) b
on a.CloseMonth = b.FeeMonth
order by a.CloseMonth

Related

Query is not grouping

Im filling a table and I need this table get the daily sells, but summing and counting, sells and clients by day respectively.
But is not grouping the sales by day this is the QUERY:
insert into VentaDiariaTienda(
Fecha,
VentaDia,
Tda_Codigo,
NumeroClientes,
PromedioVtaCliente,
EditName,
EditDateTime)
select
DATENAME(dw, T_Fecha) + ', ' +
cast(datepart(dd, T_Fecha) as char(2)) + ' ' +
datename(mm, T_Fecha) + ' ' +
cast(datepart(yyyy,T_Fecha) as char(4)),
sum(T_ImporteTotal/1.16),
[FolTda_Codigo],
count(T_Cliente),
sum(T_ImporteTotal/1.16)/count(T_Cliente),
'Admin',
GETDATE()
from #Tickets
group by T_Fecha,[FolTda_Codigo]
and this is the out of this query
Thanks for your replys.
You will have to "round down" the T_Fecha values to a day, so you might try to insert the following records:
SELECT
DATENAME(dw, dt) + ', ' +
DATENAME(dd, dt) + ' ' +
DATENAME(mm, dt) + ' ' +
DATENAME(yy, dt),
SUM(T_ImporteTotal)/1.16,
FolTda_Codigo,
COUNT(T_Cliente),
SUM(T_ImporteTotal)/1.16/COUNT(T_Cliente),
'Admin',
GETDATE()
FROM #Tickets
CROSS APPLY (
VALUES (DATEADD(day, DATEDIFF(day, 0, T_Fecha), 0))
) d (dt)
GROUP BY dt, FolTda_Codigo
My proposition:
You can use format function (since SQL Server 2012, else use Wolfgang Method)
Be carefull with divide by "Count" function. If you result is 0 (by example if T_Client is always null) you will have an error
Not necessary to use "outer apply" instruction in this case, group on by date and not by date and time
Something like this:
SELECT
FORMAT(T_Fecha, 'dddd, dd MMMM yyyy', 'en-US' ),
SUM(T_ImporteTotal)/1.16,
FolTda_Codigo,
COUNT(T_Cliente),
case when COUNT(T_Cliente)=0 then null else SUM(T_ImporteTotal)/1.16/COUNT(T_Cliente) end,
'Admin',
GETDATE()
FROM #Tickets
GROUP BY cast(T_Fecha as date), FolTda_Codigo
For SQL Server 2008, replace format by
DATENAME(dw, T_Fecha) + ', ' + DATENAME(dd, T_Fecha) + ' ' +
DATENAME(mm, T_Fecha) + ' ' + DATENAME(yy, T_Fecha),

Use MAX(Date) value inside a SQL recursion

I have SQL query with performs recursion based on selected Min(Date) and Max(Date) from more than 1 table. But when i try to run the query it throws GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'ctedaterange'. exception. I also tried using Top 1 Date but still no hope.
SQL Query
ALTER VIEW [dbo].[CYExtraction]
AS
WITH ctedaterange
AS (SELECT
[Dates] = (SELECT
MIN(CreatedOn) AS CreatedOn
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Items) AS it
UNION ALL
SELECT
*
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Bibs) AS bib
UNION ALL
SELECT
*
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Porders) AS po) AS AllItems)
UNION ALL
SELECT
[dates] + 1
FROM ctedaterange
WHERE [dates] + 1 <= (SELECT
MAX(CreatedOn) AS CreatedOn
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Items) AS it
UNION ALL
SELECT
*
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Bibs) AS bib
UNION ALL
SELECT
*
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Porders) AS po) AS AllItems))
SELECT
[Dates] AS PK_Date,
DATENAME(MONTH, [Dates]) + ' ' + CAST(YEAR([Dates]) AS varchar(4)) AS
Month,
CASE
WHEN DATEPART(m, [Dates]) <= 3 THEN 'Q1 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 6 THEN 'Q2 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 9 THEN 'Q3 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 12 THEN 'Q4 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE ''
END
END
END
END AS 'Quarter',
CASE
WHEN DATEPART(m, [Dates]) <= 6 THEN 'HY1 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 12 THEN 'HY2 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE ''
END
END AS 'HalfYear',
YEAR([Dates]) AS 'Year',
CAST('1-' AS varchar(5)) + CAST(DATEPART(M, [Dates]) AS varchar(10)) + '-' + CAST(YEAR([Dates]) AS varchar(4)) AS DateName
FROM ctedaterange
GO
The main thing is that I need to wrap it inside my SQL Views. Any help to my problem will be appreciated.
Thanks,
You can move the aggregations to an extra cte, so this might be what you want:
ALTER VIEW [dbo].[CYExtraction]
(PK_Date, [Month], [Quarter], [HalfYear], [Year], [DateName])
AS
WITH cteAllItems (mindates, maxdates) AS (
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Items
UNION ALL
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Bibs
UNION ALL
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Porders
),
ctedaterange (Dates, MaxDate) AS (
SELECT MIN(mindates), MAX(maxdates)
FROM cteAllItems
UNION ALL
SELECT DATEADD(day, 1, Dates), MaxDate
FROM ctedaterange
WHERE DATEADD(day, 1, Dates) <= MaxDate
)
SELECT
Dates AS PK_Date,
DATENAME(MONTH, Dates) + N' ' + DATENAME(YEAR, Dates),
N'Q' + DATENAME(QUARTER, Dates) + N' ' + DATENAME(YEAR, Dates),
N'HY' + CAST((MONTH(Dates)-1)/6+1 AS nvarchar(1)) + N' ' + DATENAME(YEAR, Dates),
YEAR(Dates),
CONVERT(nvarchar(10), DATEADD(MONTH, DATEDIFF(MONTH, 0, Dates), 0), 105)
FROM ctedaterange
GO

select date in form dd/mm/yyyy in sql query

in database table I got :
int day
int month
int year
I want to write a select query to show the date : dd/mm/yyyy
I try this but didn't work :
1-
select (day + '\' + month + '\' + year) as xdate from table1
but give me an error
2-
select (day + '-' + month + '-' + year) as xdate from table1
it return the sum of these field : 2036 instead of 21/1/2014
any solution ?
Try something like this:
select (cast(day as varchar(2)) + '/'
+ cast(month as varchar(2)) + '/'
+ cast(year as varchar(4))) as xdate
from table1
If TSQL / SQL-SERVER, then + only works to concatenate strings... So do some conversions on your numbers in that case:
select
CONVERT(VARCHAR, day) + '\' + CONVERT(VARCHAR, month) + '\' ... etc.
Since you are Casting int to Varchar, so you need to use CAST or CONVERT function
CAST and CONVERT
Try like this
select (Cast(day AS Varchar(10)) + '\' + Cast(month AS Varchar(10)) + '\' + Cast(year AS Varchar(10))) as xdate from table1
(Or)
select (Cast(day AS Varchar(10)) + '-' + Cast(month AS Varchar(10)) + '-' + Cast(year AS Varchar(10))) as xdate from table1
CONVERT(date, , 103) converts a date to a string representation in dd/mm/yyyy form
SELECT CONVERT(date,DATEFROMPARTS([year],[month],[day]),103)

joining two datatable which have dynamic column using stored procedure in dynamic sql

How can I join (inner) the two datatable dt1 and dt2 that have dynamic coloumn. using stored procedure in dynamic sql.
dt1 contains
emp id empname SickLeave Casualleave
1 h 1
dt2 contains
empid empname SickLeave Casualleave
1 h 5 5
I have to show output like
empid empname SickLeave Casualleave
1 h 1/5 0/5
Please guide me
Thanks
You can do this using STUFF() like this:
WITH CTE(empid, empname, SickLeave, Casualleave)
AS (SELECT * FROM dt1
UNION ALL
SELECT * FROM dt2
)
SELECT distinct empid, empname
, SickLeave =
STUFF((SELECT ' / ' + CONVERT(VARCHAR(20),[SickLeave])
FROM CTE b
WHERE b.empid = a.empid
FOR XML PATH('')), 1, 2, '')
, Casualleave =
STUFF((SELECT ' / ' + CONVERT(VARCHAR(20),[Casualleave])
FROM CTE C
WHERE C.empid = a.empid
FOR XML PATH('')), 1, 2, '')
FROM CTE a
GROUP BY Empid, empname;
Output:
| EMPID | EMPNAME | SICKLEAVE | CASUALLEAVE |
---------------------------------------------
| 1 | h | 1 / 5 | 0 / 5 |
See this SQLFiddle
You don't need dynamic SQL...
SELECT emp_id
, empname
, dt1_sickleave + '/' + dt2_sickleave As sickleave
, dt1_casualleave + '/' + dt2_casualleave As casualleave
FROM (
SELECT Coalesce(dt1.emp_id, dt2.emp_id) As emp_id
, Coalesce(dt1.empname, dt2.empname) As empname
, Cast(Coalesce(dt1.sickleave , 0) As varchar(10)) As dt1_sickleave
, Cast(Coalesce(dt1.casualleave, 0) As varchar(10)) As dt1_casualleave
, Cast(Coalesce(dt2.sickleave , 0) As varchar(10)) As dt2_sickleave
, Cast(Coalesce(dt2.casualleave, 0) As varchar(10)) As dt2_casualleave
FROM dt1
FULL
JOIN dt2
ON dt2.emp_id = dt1.emp_id
) As x
select t1.[empid],t1.[empname],
Cast(COALESCE(t1.[SickLeave],0) as char)
+'/'
+cast(COALESCE(t2.[SickLeave],0) as char) as SickLeave,
Cast(COALESCE(t1.[Casualleave],0) as char)
+'/'
+cast(COALESCE(t2.[Casualleave],0) as char) as Casualleave
from Table1 t1 inner join Table2 t2 on
t1.[empid]=t2.[empid];
fiddle
So maybe this will do what you want. This query gets all the column names from sys.columns named like '%leave%' where the table is either dt1or dt2 and builds a dynamic query that it then executes. It relies on the sys.columns and sys.tables (which might be bad) and has the source table names dt1and dt2hard coded.
In my opinion this is not a good solution, and the correct solution to the problem might be to alter the data model so that leavebecomes an entity of it's own.
I've commented out the EXECUTEat the end and left the PRINTin so you can see what the query will do before executing it.
DECLARE #Columns VARCHAR(MAX)
SELECT #Columns = COALESCE(#Columns + ',' + name + '', '' + name + '') FROM (
SELECT DISTINCT
'CAST(ISNULL(dt1.' + name + ',0) AS VARCHAR) + ''/'' + CAST(dt2.' + name + ' AS VARCHAR) AS ' + name + ' ' AS name
FROM sys.columns
WHERE NAME LIKE '%leave%'
AND object_id IN (SELECT object_id FROM sys.tables WHERE name IN ('dt1', 'dt2'))) LeaveColumns
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
SELECT
dt1.empid,
dt1.empname, ' + #Columns + '
FROM dt1
INNER JOIN dt2 ON dt1.empid=dt2.empid'
PRINT #SQL -- Uncomment to see the query which will be run
--EXECUTE(#SQL)
For me this gives the following output (with a slightly altered table structure where I included more leavecolumns):
SELECT
dt1.empid,
dt1.empname,
CAST(ISNULL(dt1.casualleave,0) AS VARCHAR) + '/' + CAST(dt2.casualleave AS VARCHAR) AS casualleave ,
CAST(ISNULL(dt1.sickleave,0) AS VARCHAR) + '/' + CAST(dt2.sickleave AS VARCHAR) AS sickleave ,
CAST(ISNULL(dt1.someotherleave,0) AS VARCHAR) + '/' + CAST(dt2.someotherleave AS VARCHAR) AS someotherleave ,
CAST(ISNULL(dt1.yetanotherleave,0) AS VARCHAR) + '/' + CAST(dt2.yetanotherleave AS VARCHAR) AS yetanotherleave
FROM dt1
INNER JOIN dt2 ON dt1.empid=dt2.empid

Conditional Common Table Expression (CTE) in SQL

I'm trying to select the hierarchy of a product category tree in SQL.
My code looks as follows. I'm trying to achieve a dynamic sort order, using IF or Case When on the SortOrder parameter.
The commented line should be active if #SortOrder is equal to 'sortorder'. I tried to add If Else statement around it, but I failed...
Can you help?
CREATE PROCEDURE [dbo].[ProductCategory_SelectHierarchy]
#SortOrder varchar(30)
AS
SET NOCOUNT ON;
WITH Categories (Id,ParentId,SortOrder,RowOrder) as
(
SELECT parentCategory.Id,
parentCategory.ParentId,
parentCategory.SortOrder,
--cast(REPLACE(STR(parentCategory.SortOrder, 8), ' ', '0') as varchar(30)) 'RowOrder'
cast(CAST(DATEPART(YEAR, parentCategory.DateCreated) as varchar(4)) +
CAST(DATEPART(MONTH, parentCategory.DateCreated) as varchar(2)) +
CAST(DATEPART(DD, parentCategory.DateCreated) as varchar(2)) +
CAST(DATEPART(HOUR, parentCategory.DateCreated) as varchar(2)) as varchar(50)) 'RowOrder'
FROM ProductCategories parentCategory
WHERE ParentId = 0
UNION ALL
SELECT childCategories.Id,
childCategories.ParentId,
childCategories.SortOrder,
--cast(Categories.RowOrder + REPLACE(STR(childCategories.SortOrder, 8), ' ', '0') as varchar(30)) 'RowOrder'
cast(Categories.RowOrder + '/' + CAST(DATEPART(YEAR, childCategories.DateCreated) as varchar(4)) +
CAST(DATEPART(MONTH, childCategories.DateCreated) as varchar(2)) +
CAST(DATEPART(DD, childCategories.DateCreated) as varchar(2)) +
CAST(DATEPART(HOUR, childCategories.DateCreated) as varchar(2)) as varchar(50)) 'RowOrder'
FROM ProductCategories childCategories
JOIN Categories
ON childCategories.ParentId = Categories.Id
)
SELECT pc.*, Categories.RowOrder
FROM Categories
INNER JOIN ProductCategories pc ON pc.Id = Categories.Id
ORDER BY RowOrder
You should be able to sort it like so:
ORDER BY
CASE
WHEN #SortOrder = 'date_column' THEN CONVERT(VARCHAR(20), date_column, 120)
WHEN #SortOrder = 'customer_id' THEN RIGHT(REPLICATE('0', 20) + CAST(customer_id AS VARCHAR(20)), 20)
WHEN #SortOrder = 'name' THEN name
ELSE sort_order
END
The key is getting all of your sortable columns (or expressions) to end up as the same data type.

Resources