Different Output between Stored Procedure and Direct Query - sql-server

I have a Stored Procedure that has been working fine for about 8 months of use in preparing data to be sent off to another service. Two days ago it started truncating the results but there hasn't been any changes to the code in that time period.
Here's the weird part, if I take the statements out of the stored procedure and don't change anything other than making it a direct query, I get the expected result.
Stored Procedure: 5481 Rows
Direct Query: 7490 Rows
I've tried dropping the execution cache, bouncing the server and services but that doesn't fix it. I have also setup a temporary table to capture any of the prepared batches within the Stored Procedure to compare against the direct query and those all match.
I'm at a loss as to how this is even possible.
Edit:
Code Added Here
CREATE PROCEDURE [ETL].[spDeliverTransactionalFile]
#TransactionDate DATE
,#MonthNum INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #FiscalPeriodShort VARCHAR(10)
,#FiscalQuarterShort VARCHAR(9)
,#CalendarMonth INT
CREATE TABLE #MonthNum (
FiscalPeriodShort VARCHAR(10)
,MaxDate DATE
,MonthNum INT
)
IF #TransactionDate IS NULL OR #TransactionDate = '2006-12-31'
BEGIN
INSERT INTO #MonthNum
SELECT FiscalPeriodShort
,MAX(DateID) AS MaxDate
,ROW_NUMBER() OVER (ORDER BY MAX(DateID) ASC) AS MonthNum
FROM dimDate
WHERE FiscalQuarterShort = (
SELECT FiscalQuarterShort
FROM dimDate
WHERE DateID = dbo.TransactionDateID(NULL)
)
GROUP BY FiscalPeriodShort
END
ELSE
BEGIN
INSERT INTO #MonthNum
SELECT FiscalPeriodShort
,MAX(DateID) AS MaxDate
,ROW_NUMBER() OVER (ORDER BY MAX(DateID) ASC) AS MonthNum
FROM dimDate
WHERE FiscalQuarterShort = (
SELECT FiscalQuarterShort
FROM dimDate
WHERE DateID = #TransactionDate
)
GROUP BY FiscalPeriodShort
END
SELECT #FiscalPeriodShort = FiscalPeriodShort
FROM #MonthNum
WHERE MonthNum = #MonthNum
SELECT #CalendarMonth = MIN(CalendarMonth)
FROM dimDate
WHERE FiscalPeriodShort = #FiscalPeriodShort
SELECT CONVERT(VARCHAR(20),l.OrderId) AS OrderCode
,CONVERT(VARCHAR(10),ROW_NUMBER() OVER (PARTITION BY l.OrderID ORDER BY dp.Model, dr.RebateID)) + '-' + CONVERT(VARCHAR(10),l.factBookingLineSID) AS ItemCode
,'Sell' + #FiscalPeriodShort + '_' + CONVERT(VARCHAR(2),#CalendarMonth) AS BatchName
,'Sell' AS BatchType
,REPLACE(REPLACE(dp.Model, ', ', '-'), ',', '-') AS ProductName
,REPLACE(REPLACE(dst.SalesTeamName, ', ', '-'), ',', '-') AS GeographyName
,REPLACE(REPLACE(dc.CustomerName, ', ', '-'), ',', '-') AS CustomerName
,SUM(l.CurrentUnitQuantity) AS Quantity
,SUM(l.CurrentLineTotal) AS Amount
,'USD' AS AmountUnitType
,CONVERT(VARCHAR(10),dd.DateID,101) AS IncentiveDate
,'Sell' AS OrderType
,dst.SalesTeamID
,CASE WHEN dp.ProductGroupCode NOT IN ('Prod1','Prod2','Prod3','Prod4') THEN 'Group1'
WHEN dp.ProductGroupCode = 'Prod3' AND l.FranchiseSID IN (1,3) THEN 'Group1'
WHEN dp.ProductGroupCode = 'Prod4' AND l.FranchiseSID IN (1,3) THEN 'Group1'
WHEN dp.ProductGroupCode IN ('Prod1','Prod2') THEN 'Group2'
WHEN dp.ProductGroupCode = 'Prod3' AND l.FranchiseSID = 2 THEN 'Group2'
WHEN dp.ProductGroupCode = 'Prod4' AND l.FranchiseSID = 2 THEN 'Group2'
ELSE 'UKWN'
END AS Product_Type
FROM factTransactionLine l
INNER JOIN dimDate dd ON l.TransactionDateSID = dd.DateSID
INNER JOIN dimProduct dp ON l.ProductLotSID = dp.ProductLotSID
INNER JOIN dimSalesTeam dst ON l.CurrentSalesTeamSID = dst.SalesTeamSID
INNER JOIN dimCustomer dc ON l.CustomerSID = dc.CustomerSID
WHERE l.Deleted = 0
AND dd.FiscalPeriodShort = #FiscalPeriodShort
GROUP BY l.OrderId
,dp.PartNumber
,dp.Model
,dc.CustomerName
,dd.DateID
,dst.SalesTeamName
,dst.SalesTeamID
,dp.ProductGroupCode
,l.FranchiseSID
,l.factTransactionLineSID
ORDER BY OrderCode
,ItemCode
DROP TABLE #MonthNum
END
At Which Point I am running the Stored Procedure using the following syntax:
EXEC etl.spDeliverTransactionalFile #TransactionDate = '2018-03-14', #MonthNum = 1
Which returns 5481 Rows.
But If I take the Query out of the Stored Procedure as follows:
DECLARE #TransactionDate DATE
,#MonthNum INT
SELECT #TransactionDate = '2018-03-14'
,#MonthNum = 1
DECLARE #FiscalPeriodShort VARCHAR(10)
,#FiscalQuarterShort VARCHAR(9)
,#CalendarMonth INT
CREATE TABLE #MonthNum (
FiscalPeriodShort VARCHAR(10)
,MaxDate DATE
,MonthNum INT
)
IF #TransactionDate IS NULL OR #TransactionDate = '2006-12-31'
BEGIN
INSERT INTO #MonthNum
SELECT FiscalPeriodShort
,MAX(DateID) AS MaxDate
,ROW_NUMBER() OVER (ORDER BY MAX(DateID) ASC) AS MonthNum
FROM dimDate
WHERE FiscalQuarterShort = (
SELECT FiscalQuarterShort
FROM dimDate
WHERE DateID = dbo.TransactionDateID(NULL)
)
GROUP BY FiscalPeriodShort
END
ELSE
BEGIN
INSERT INTO #MonthNum
SELECT FiscalPeriodShort
,MAX(DateID) AS MaxDate
,ROW_NUMBER() OVER (ORDER BY MAX(DateID) ASC) AS MonthNum
FROM dimDate
WHERE FiscalQuarterShort = (
SELECT FiscalQuarterShort
FROM dimDate
WHERE DateID = #TransactionDate
)
GROUP BY FiscalPeriodShort
END
SELECT #FiscalPeriodShort = FiscalPeriodShort
FROM #MonthNum
WHERE MonthNum = #MonthNum
SELECT #CalendarMonth = MIN(CalendarMonth)
FROM dimDate
WHERE FiscalPeriodShort = #FiscalPeriodShort
SELECT CONVERT(VARCHAR(20),l.OrderId) AS OrderCode
,CONVERT(VARCHAR(10),ROW_NUMBER() OVER (PARTITION BY l.OrderID ORDER BY dp.Model, dr.RebateID)) + '-' + CONVERT(VARCHAR(10),l.factBookingLineSID) AS ItemCode
,'Sell' + #FiscalPeriodShort + '_' + CONVERT(VARCHAR(2),#CalendarMonth) AS BatchName
,'Sell' AS BatchType
,REPLACE(REPLACE(dp.Model, ', ', '-'), ',', '-') AS ProductName
,REPLACE(REPLACE(dst.SalesTeamName, ', ', '-'), ',', '-') AS GeographyName
,REPLACE(REPLACE(dc.CustomerName, ', ', '-'), ',', '-') AS CustomerName
,SUM(l.CurrentUnitQuantity) AS Quantity
,SUM(l.CurrentLineTotal) AS Amount
,'USD' AS AmountUnitType
,CONVERT(VARCHAR(10),dd.DateID,101) AS IncentiveDate
,'Sell' AS OrderType
,dst.SalesTeamID
,CASE WHEN dp.ProductGroupCode NOT IN ('Prod1','Prod2','Prod3','Prod4') THEN 'Group1'
WHEN dp.ProductGroupCode = 'Prod3' AND l.FranchiseSID IN (1,3) THEN 'Group1'
WHEN dp.ProductGroupCode = 'Prod4' AND l.FranchiseSID IN (1,3) THEN 'Group1'
WHEN dp.ProductGroupCode IN ('Prod1','Prod2') THEN 'Group2'
WHEN dp.ProductGroupCode = 'Prod3' AND l.FranchiseSID = 2 THEN 'Group2'
WHEN dp.ProductGroupCode = 'Prod4' AND l.FranchiseSID = 2 THEN 'Group2'
ELSE 'UKWN'
END AS Product_Type
FROM factTransactionLine l
INNER JOIN dimDate dd ON l.TransactionDateSID = dd.DateSID
INNER JOIN dimProduct dp ON l.ProductLotSID = dp.ProductLotSID
INNER JOIN dimSalesTeam dst ON l.CurrentSalesTeamSID = dst.SalesTeamSID
INNER JOIN dimCustomer dc ON l.CustomerSID = dc.CustomerSID
WHERE l.Deleted = 0
AND dd.FiscalPeriodShort = #FiscalPeriodShort
GROUP BY l.OrderId
,dp.PartNumber
,dp.Model
,dc.CustomerName
,dd.DateID
,dst.SalesTeamName
,dst.SalesTeamID
,dp.ProductGroupCode
,l.FranchiseSID
,l.factTransactionLineSID
ORDER BY OrderCode
,ItemCode
DROP TABLE #MonthNum
I will get the correct data at 7490 rows.

This
SELECT FiscalQuarterShort
FROM dimDate
WHERE DateID = dbo.TransactionDateID(NULL)
Is non-deterministic
Why not just reassign #TransactionDate rather than repeat all that code?

So it turns out to be a Fully Qualified issue.
There was another table added under another schema for dimProduct at some point. When running the query as is, it was defaulting to dbo. When running it under the stored procedure it was running it under the alternate schema which is used for ETL.
I always thought that if a schema wasn't defined the Query Engine would default to dbo, but that wasn't the case here.

Related

Using DECLARE while creating a VIEW?

The literature says that the declare statement is not compatible with creating a View. How do I get around it?
My declare statement looks like:
DECLARE #risk_5 TABLE (Code VARCHAR(100));
INSERT INTO #risk_5 (Code) VALUES ('AA'),('BB'),('CC');
and is then used within a select statement:
SELECT
id,
CASE
WHEN a.[10_2_1_Country] IN (SELECT Code from #risk_5)
THEN '3'
END AS Risk_Country5
FROM x
The recommendation is to pack the declare into a CTE or a stored procedure.
With both these recommendations though, I do not understand how to connect the two? What am I missing?
If you need to use variable try to use stored procedures, if you write a select query in the stored procedure you can get the data too. And you can use declare inside.
I use this way in my solution e.g.
CREATE PROCEDURE [dbo].[GetLoadSiteMass]( #month INT,
#year int,
#storageId int,
#parent nvarchar(50),
#materialSourceId nvarchar(100),
#complexIds nvarchar(50))
AS
BEGIN
DECLARE #MonthPrev int
DECLARE #YearPrev int
SET #MonthPrev = CASE WHEN #Month = 1 THEN 12 ELSE #Month - 1 END
SET #YearPrev = CASE WHEN #Month = 1 THEN #Year - 1 ELSE #Year END
declare #WagonLoadSiteId int
set #WagonLoadSiteId = (select top 1 CarriageLoadSiteId from CarriageLoadSite where LoadSiteType = 2);
DECLARE #loadSide nvarchar(10), #result decimal(18,3)
SET #loadSide=cast( #storageId as nvarchar(50));
WITH CarriageLoadSiteTreeView (
[CarriageLoadSiteId],RootId,RootName,[Code], Name, ParentID, [LoadSiteType],IsDelete,
CodeSAP,DepartmentId, Capacity, MinLimit, MaxLimit, LoadSitePlaceTypeId) AS
(
SELECT [CarriageLoadSiteId],
[CarriageLoadSiteId] RootId,
Name RootName,
[Code],
Name,
ParentID,
[LoadSiteType],
[IsDelete],
CodeSAP,
DepartmentId,
Capacity,
MinLimit,
MaxLimit,
LoadSitePlaceTypeId
FROM CarriageLoadSite WITH(NOLOCK)
WHERE ISNULL(ParentID,0) =isnull(#storageId,0) AND Isdelete!=1
UNION ALL
SELECT d.[CarriageLoadSiteId],
q.RootId RootId,
RootName RootName,
d.[Code],
d.Name,
d.ParentID,
d.[LoadSiteType],
d.[IsDelete],
d.CodeSAP,
d.DepartmentId,
d.Capacity,
d.MinLimit,
d.MaxLimit,
d.LoadSitePlaceTypeId
FROM CarriageLoadSite AS d WITH(NOLOCK)
INNER JOIN CarriageLoadSiteTreeView AS q ON d.ParentID = q.[CarriageLoadSiteId] WHERE d.IsDelete!=1
)
SELECT
ComplexId,
RootId Id,
cast(RootId as nvarchar(8))+'|Sclad'+IIF(RootId=max(R.CarriageLoadSiteId),'|finish','') [Uid],
RootName CarriageLoadSiteName,
ROUND(SUM(AMOUNT-movement-consumption)/1000,3) Amount,
cast(1 as bit) hasChildren,
T.FullPathId Path,
UparentId=IIF(#parent is null,'',#parent),
[Type]=0,
Petal = IIF(RootId=max(R.CarriageLoadSiteId),'|Sclad|finish','')
FROM (
SELECT
RootId
,RootName
,t.CarriageLoadSiteId
,t.MaterialId
,YEAR(t.Period) [Year]
,MONTH(t.Period) [Month]
,round( case when (t.Amount=0 or t.Amount is null) and (tt.Type=0 or [TypeAmountCarriage]=1 )then carr.[CertifNetto]else t.Amount end ,0 )[Amount]
,t.UnitId
, CarriageId
, tt.TurnoverTypeId
,round(dbo.GetMovementByTurnOverWithTempValue(t.turnoverid),5) movement
,dbo.GetConsumptionByTurnOver(t.turnoverid) consumption
,0 stockBegin
,round(t.Amount,0 ) CommingAmount
,case when (t.Amount=0 or t.Amount is null) and tt.Type=0 then 1 else 0 end [IsNotConfirmed]
,[TypeAmountCarriage]
,M.ComplexId
FROM Turnover t WITH(NOLOCK)
INNER JOIN TurnoverType tt ON tt.TurnoverTypeId = t.TurnoverTypeId
INNER JOIN CarriageLoadSiteTreeView l ON l.CarriageLoadSiteId = t.CarriageLoadSiteId
INNER JOIN [Carriages] carr on carr.[CarriagesID]=t.[CarriageId]
INNER JOIN Material M on M.MaterialID=t.MaterialId
WHERE YEAR(t.Period) = #Year AND
MONTH(t.Period) = #Month AND
l.LoadSiteType = 0 AND
tt.type in (0,5,4) AND
isclear=0 AND
M.MaterialSourceID in (select value from string_split(#materialSourceId, ','))
UNION ALL
SELECT RootId
,RootName
,s.CarriageLoadSiteId
,s.MaterialId
,#Year [Year]
,#Month [Month]
,round(s.Amount,0)
,s.UnitId
,CarriageId
,[TurnoverTypeId]
,round(dbo.GetMovementByStock(s.StockId),5) movement
,dbo.GetConsumptionByStock(s.StockId) consumption
,round(s.Amount,0)-s.spendStock
,0
,0 [IsNotConfirmed]
,[TypeAmountCarriage]
,M.ComplexId
FROM Stock s
INNER JOIN CarriageLoadSiteTreeView l ON l.CarriageLoadSiteId = s.CarriageLoadSiteId
INNER JOIN Material M on M.MaterialID=s.MaterialId
WHERE s.[Year] = #YearPrev AND
s.[Month] = #MonthPrev AND
s.[Type] = 0 AND
l.LoadSiteType = 0 AND
amount >0 AND
isclear=0 AND
M.MaterialSourceID in (select value from string_split(#materialSourceId, ','))
) as R
INNER JOIN CariageLoadSiteTree T on T.CarriageLoadSiteId=RootId
INNER JOIN string_split(#complexIds, ',') MM ON CAST(MM.value AS int) = R.ComplexId
WHERE AMOUNT-movement-consumption>10
GROUP BY RootName,RootId,ComplexId, T.FullPathId
ORDER BY RootName

Return value if no data exist in a list sql server

I have a list of barcode and want to get the InvtID with Bookcode as a column. I want if there is no barcode exists then it will return 'no data' as well in the list. This is my current query but it only display the list of exist barcode only.
> WITH cte AS (SELECT *, ROW_NUMBER() OVER(PARTITION BY Barcode ORDER BY
InvtID Asc) rid FROM InvtCust WHERE Barcode In('123','9789830093697','9789830093727',)) SELECT InvtID,
BOOKCODE = coalesce(InvtID, 'Bookcode not found') FROM cte WHERE rid =
1 UNION SELECT InvtID = '', BOOKCODE = 'Bookcode not found' WHERE NOT
EXISTS(SELECT 1 FROM CTE)
I know there's a lot of question regarding this matter. But I really couldnt solve mine. I have tried these none of it working.
SELECT isnull((SELECT [InvtID]
FROM InvtCust WHERE Barcode IN('123','9789830093819')),'No bookcode found')
AS InvtID
Select case when s.InvtID IS NOT NULL Then s.InvtID else 'no data' end as Bookcode
from (Select InvtID as InvtID FROM InvtCust WHERE Barcode IN('123')) R
Left Join InvtCust s ON s.InvtID = R.InvtID
select expr1, InvtID from (
select *,ROW_NUMBER() over(order by isrealrow asc) rownum from (
select COUNT(*) as expr1, InvtID, 1 as isrealrow from [DANNY].[dbo].[InventoryCustomer]
where Barcode in ('ean','9789830093819','9789830093826','9789830094205')
Group by InvtID
union select 0,'No bookcode found',0 as isrealrow
)b
)c
where isrealrow=1 or rownum=1
IF NOT EXISTS (Select InvtID From InvtCust WHERE Barcode in ('123'))
Begin SELECT 'Bookcode not found' as Bookcode end
ELSE
SELECT InvtID From InvtCust WHERE Barcode in ('123')
In my view, it is better to put all your logic inside stored procedure:
CREATE PROCEDURE YourProcedure
AS
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
create table #Results
(
InvtID INT,
BookCode VARCHAR(255)
)
WITH cte AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY Barcode ORDER BY InvtID Asc) rid
FROM InvtCust
WHERE Barcode In('123','9789830093697','9789830093727'))
INSERT INTO #Results
(
InvtID,
BookCode
)
SELECT InvtID,
BOOKCODE = coalesce(InvtID, 'Bookcode not found')
FROM cte WHERE rid = 1
UNION
SELECT
InvtID = ''
, BOOKCODE = 'Bookcode not found'
WHERE NOT
EXISTS(SELECT 1 FROM CTE)
DECLARE #rows INT = 0;
SELECT #rows = COUNT(1) FROM #Results
SELECT #rows
IF #rows > 0
BEGIN
SELECT
*
#Results
END
ELSE
SELECT 'No Data'
GO
This query will give you the desired results. Am Assuming you have comma separated barcode list which you want to search in the table, If the query will find that barcode in the table it will return the barcode along with the InvtID else it will return the message 'no barcode found' for that record instead of InvtID .
For SQL server 2016 or Above
with BarcodeList as
(
select value as barcode
from string_split('aaaa,bbbb,cccc' , ',')
)
Select isnull(cast(InvtID as varchar), 'No barcode found') InvtID, b.Barcode From BarcodeList b left outer join InvtCust i
on b.barcode = i.barcode
Note: string_split function used in this query supported in SQL server 2016 onwards. If you are using SQL server below 2016 then you should create & use the custom split function as shown below.
For Below SQL server 2016
Custom Split Function
create FUNCTION [dbo].[String_Split]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(value NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (value)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
Go
Actual query
with BarcodeList as
(
select value as barcode
from dbo.[String_Split]('aaaa,bbbb,cccc' , ',')
)
Select isnull(cast(InvtID as varchar), 'No barcode found') InvtID, b.Barcode From BarcodeList b left outer join InvtCust i
on b.barcode = i.barcode

T-SQL: Function that returns multiple queries from a loop

I am trying to create a function that receives as a parameter a start date and a date end of a period and results in a list totaled with the top 5 products sold for each month of the period.
For example:
CREATE FUNCTION top5Sales(#CurrentDate DATETIME, #EndDate DATETIME)
RETURNS TABLE
RETURN
(
WHILE(#CurrentDate < #EndDate)
BEGIN
SELECT TOP 5
AdventureWorks.Sales.SalesOrderDetail.ProductID,
AdventureWorks.Production.Product.Name,
MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Month',
YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Year',
SUM(AdventureWorks.Sales.SalesOrderDetail.OrderQty) as 'Total Quantity Sold',
AVG(AdventureWorks.Sales.SalesOrderDetail.UnitPrice) as 'Average Unit Price',
SUM(AdventureWorks.Sales.SalesOrderDetail.UnitPriceDiscount) as 'Total Discount',
SUM(AdventureWorks.Sales.SalesOrderDetail.LineTotal) as 'Total Value Sold'
FROM
AdventureWorks.Sales.SalesOrderDetail
INNER JOIN
AdventureWorks.Sales.SalesOrderHeader
ON
AdventureWorks.Sales.SalesOrderHeader.SalesOrderID = AdventureWorks.Sales.SalesOrderDetail.SalesOrderID
INNER JOIN
AdventureWorks.Production.Product
ON
AdventureWorks.Production.Product.ProductID = AdventureWorks.Sales.SalesOrderDetail.ProductID
WHERE
MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) = month(#CurrentDate)
and
YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) = year(#CurrentDate)
GROUP BY
AdventureWorks.Production.Product.Name,
AdventureWorks.Sales.SalesOrderDetail.ProductID,
MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate),
YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate)
ORDER BY
[Total Quantity Sold] DESC
SET
#CurrentDate = DATEADD(MONTH, 1, #CurrentDate)
END
)
In fact, the above code is wrong. It serves only to exemplify the problem and give the path to some possible solution.
You do not have to follow that line of thinking. If there is another more interesting solution to this problem, feel free to expose it.
You can use row_number and top 5 with ties and use single query to return this results as below: Can you check this?
CREATE FUNCTION top5Sales(#CurrentDate DATETIME, #EndDate DATETIME)
RETURNS TABLE
RETURN
(
SELECT Top 5 with ties * from (
SELECT
AdventureWorks.Sales.SalesOrderDetail.ProductID,
AdventureWorks.Production.Product.Name,
MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Month',
YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Year',
SUM(AdventureWorks.Sales.SalesOrderDetail.OrderQty) as 'Total Quantity Sold',
AVG(AdventureWorks.Sales.SalesOrderDetail.UnitPrice) as 'Average Unit Price',
SUM(AdventureWorks.Sales.SalesOrderDetail.UnitPriceDiscount) as 'Total Discount',
SUM(AdventureWorks.Sales.SalesOrderDetail.LineTotal) as 'Total Value Sold'
FROM
AdventureWorks.Sales.SalesOrderDetail
INNER JOIN
AdventureWorks.Sales.SalesOrderHeader
ON
AdventureWorks.Sales.SalesOrderHeader.SalesOrderID = AdventureWorks.Sales.SalesOrderDetail.SalesOrderID
INNER JOIN
AdventureWorks.Production.Product
ON
AdventureWorks.Production.Product.ProductID = AdventureWorks.Sales.SalesOrderDetail.ProductID
WHERE
AdventureWorks.Sales.SalesOrderHeader.OrderDate > #CurrentDate
AND AdventureWorks.Sales.SalesOrderHeader.OrderDate <= #EndDate
GROUP BY
AdventureWorks.Production.Product.Name,
AdventureWorks.Sales.SalesOrderDetail.ProductID,
MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate),
YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate)
-- ORDER BY
-- [Total Quantity Sold] DESC
) a
Order by Row_Number() over(Partition by [ProductId], [Name], [Year], [Month] order by [Total Quantity Sold] Desc
)
I checked the AdventureWorks database and I didn't see a Date table. For a basic overview, you can checkout this video - https://www.brentozar.com/training/t-sql-level/3-number-date-tables-10m/
The following logic wouldn't be in an inline-table function, but it's a fast and simple way of SELECTing the data you're looking for.
DECLARE #sql NVARCHAR(Max),
#CurrentDate DATETIME = '2005-06-01 00:00:00.000',
#EndDate DATETIME = '2008-08-31 00:00:00.000';
SELECT #sql = COALESCE( #sql + CHAR(13) + 'UNION' + CHAR(13), '') +
' SELECT TOP 5 ' + CONVERT(NVARCHAR(8), [Year]) + ' AS [Year], '
+ CONVERT(NVARCHAR(8), [Month]) + ' AS [Month],
sod.ProductId,
SUM(sod.OrderQty) AS Sales
FROM Sales.SalesOrderDetail sod
JOIN Sales.SalesOrderHeader soh
ON sod.SalesOrderId = soh.SalesOrderId
AND YEAR(soh.OrderDate) = ' + CONVERT(NVARCHAR(8), [Year]) + '
AND MONTH(soh.OrderDate) = ' + CONVERT(NVARCHAR(8), [Month]) + '
GROUP BY sod.ProductId'
FROM dbo.[Date]
WHERE [Date] BETWEEN #CurrentDate AND #EndDate
EXEC sp_executesql #sql
If you execute this T-SQL you'll find it's very fast and scalable relative to other methods of getting the same data.

Select multiple columns but group by only one

Using SQL Server 2008. I have been researching this problem for days. Thought about CTE and trying to use a predicate on the problem tables.
Nothing I have tried or researched has worked so here I am. The problem is it's returning duplicate OrderID.
I've marked the problem joins.
Even tried OUTER APPLY but it caused certain searches not to work. Tried INNER APPLY then duplicates again.
The problem joins, the tables, have multiple references to OrderID.
So tblRun has multiple rows with the same OrderID showing which run it was in at and what date and so forth.
I really need suggestions from all the guru's out there.
Here is the SQL:
DECLARE #CompanyID INT = 22718,
#StartDate DATETIME = '',
#EndDate DATETIME = '',
#SalesRepID INT = NULL,
#AssignedTo INT = NULL,
#ServiceDefID INT = NULL,
#ProductName VARCHAR(512) = NULL,
#IsCCOrder BIT = NULL,
#OrderID INT = NULL,
#LocationID INT = NULL,
#SalesRepLocationID INT = NULL,
#PONum VARCHAR(150) = NULL,
#InvoiceID INT = NULL,
#IsSplitOrder BIT = NULL,
#ContactID INT = NULL,
#ContactName VARCHAR(150) = NULL,
#JobName VARCHAR(200) = NULL,
#Notes VARCHAR(MAX) = NULL,
#CompanyName VARCHAR(255) = NULL,
#DueDateFrom DATETIME = '',
#DueDateTo DATETIME = '',
#SubmitedDateFrom DATETIME = '',
#SubmitedDateTo DATETIME = '',
#IsRush BIT = NULL,
#Msg VARCHAR(1000) = NULL
--#Stages dbo.int_tbltype READONLY
DECLARE #Stages TABLE (ID INT)
--INSERT INTO #Stages (ID) VALUES (1)
DECLARE #DueDate DATETIME = NULL
SET NOCOUNT ON
DECLARE #OrderIDsTBL TABLE(OrderID INT)
IF #Msg IS NOT NULL
BEGIN
INSERT INTO #OrderIDsTBL (OrderID)
SELECT OrderID
FROM tblOrderLog
WHERE Msg LIKE '%' + #Msg + '%'
END
IF #OrderID IS NOT NULL
BEGIN
INSERT INTO #OrderIDsTBL (OrderID)
VALUES (#OrderID)
END
DECLARE #OderIDsCnt INT = (SELECT COUNT(OrderID) FROM #OrderIDsTBL)
DECLARE #StageCnt INT = (SELECT COUNT(ID) FROM #Stages)
SELECT
o.OrderID,
o.CompanyID,
co.Name AS CompanyName,
o.ContactID,
o.JobName,
p.FirstName + ' ' + p.LastName AS ContactName,
p2.FirstName + ' ' + p2.LastName AS SalesRep,
o.DueDate,
CASE WHEN MAX(oi.PriorityService) > 0 THEN 1 ELSE 0 END AS IsRush,
ISNULL(s.StageID, 0) AS StageID,
o.Notes, r.SubmittedComplete,
dbo.fOrderRunLocationCSVByOrderID(o.OrderID) AS LocationCSV,
(SELECT
STUFF((SELECT DISTINCT ' ' + st.Name + '<br />' FROM tblStage st
INNER JOIN tblOrderItem oi ON oi.OrderID = o.OrderID
INNER JOIN tblRun r ON r.OrderItemID = oi.OrderItemID
INNER JOIN tblStage s ON s.StageID = r.StageID
LEFT JOIN tblRunService rs ON rs.RunID = r.RunID
WHERE (s.StageID = st.StageID)
AND (rs.AssignedTo = #AssignedTo OR #AssignedTo IS NULL)
FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, ''))
AS Stages,
Row_Number() Over(Order By o.OrderID Desc) As RowNum
FROM
tblOrder o
INNER JOIN
tblCompany co ON co.CompanyID = o.CompanyID
INNER JOIN
tblParty p ON p.PartyID = o.ContactID
-------- PROBLEM JOINS ------------
LEFT JOIN
tblOrderItem oi ON oi.OrderID = o.OrderID
LEFT JOIN
tblRun r ON r.OrderItemID = oi.OrderItemID
LEFT JOIN
tblService srv ON srv.OrderItemID = oi.OrderItemID
-------- END PROBLEM JOINS ------------
LEFT JOIN
tblStage s ON s.StageID = r.StageID
LEFT JOIN
tblParty p2 ON p2.PartyID = o.SalesRepID
LEFT JOIN
tblEmployee e ON e.EmployeeID = o.SalesRepID
LEFT JOIN
tblShipTo st ON o.ShipToID = st.ShipToID
WHERE
(#CompanyID IS NULL OR (o.CompanyID = #CompanyID )) AND
(#IsCCOrder IS NULL OR (ISNULL(o.IsCreditCardOrder, 0) = #IsCCOrder )) AND
(#SalesRepID IS NULL OR o.SalesRepID = #SalesRepID) AND
(#ServiceDefID IS NULL OR (srv.ServiceDefID = #ServiceDefID)) AND
(#ProductName IS NULL OR (oi.Name LIKE '%' + #ProductName + '%')) AND
(#IsSplitOrder IS NULL OR (#IsSplitOrder = 1 AND oi.SplitOrderID IS NOT NULL)) AND
(
(#StartDate = '' OR #EndDate = '') OR
(#StartDate >= CreatedDate AND #EndDate <= COALESCE(CancelledDate, ClosedDate, GetDate())) OR
(#StartDate <= COALESCE(CancelledDate, ClosedDate, GETDATE()) AND #EndDate >= COALESCE(CancelledDate, ClosedDate, GetDate()) ) OR
(#StartDate <= CreatedDate AND #EndDate >= CreatedDate )
) AND
(#LocationID IS NULL OR (#LocationID = srv.LocationID OR srv.LocationID IS NULL)) AND
(#SalesRepLocationID IS NULL OR (#SalesRepLocationID = e.LocationID OR e.LocationID IS NULL))
AND (#InvoiceID IS NULL OR o.InvoiceID = #InvoiceID )
AND (#PONum IS NULL OR o.PONum LIKE '%' + #PONum + '%')
AND (COALESCE(s.StageID, 0) IN (SELECT ID FROM #Stages) OR #StageCnt = 0)
AND (o.ContactID = #ContactID OR #ContactID IS NULL)
AND (p.FirstName + ' ' + p.LastName LIKE '%' + #ContactName + '%' OR #ContactName IS NULL)
AND (o.JobName LIKE '%' + #JobName + '%' OR #JobName IS NULL)
AND (o.Notes LIKE '%' + #Notes + '%' OR #Notes IS NULL)
AND (co.Name LIKE '%' + #CompanyName + '%' OR #CompanyName IS NULL)
AND (o.DueDate >= #DueDateFrom OR #DueDateFrom = '')
AND (o.DueDate <= #DueDateTo OR #DueDateTo = '')
AND (r.SubmittedComplete >= #SubmitedDateFrom OR #SubmitedDateFrom = '')
AND (r.SubmittedComplete <= #SubmitedDateTo OR #SubmitedDateTo = '')
AND (#IsRush = (CASE WHEN oi.PriorityService > 0 THEN 1 ELSE 0 END)
OR #IsRush IS NULL)
AND (o.OrderID IN (SELECT OrderID FROM #OrderIDsTBL) OR #OderIDsCnt = 0)
GROUP BY
o.OrderID, o.CompanyID,
co.Name,
o.ContactID, o.JobName,
p.FirstName, p.LastName, p2.FirstName, p2.LastName,
o.DueDate, o.Notes,
r.SubmittedComplete,
s.StageID
Thanks for any suggestions. I've been working on this for some time now and just can't get it working right.
It looks like you're trying to do too much with a single SELECT statement. If you want one row per unique OrderID, then don't join with the tables that have multiple rows for the same OrderID. Remove the GROUP BY clause. Use one or more separate SELECT statements to get the details from the tables that have multiple rows per OrderID.
Thanks everyone for the suggestions but I found my own solution.
With the entire sql, I placed it into a temp table.
Then used this to sort out the duplicate OrderIds....
SELECT OrderID, CompanyID, ContactID, CompanyName, JobName, ContactName,
SalesRep, DueDate, IsRush , StageID, Notes, SubmittedComplete,
LocationCSV, Stages
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY OrderID ORDER BY OrderID DESC) 'RowRank'
FROM #SearchTbl
)sub
WHERE RowRank = 1

How do I order weekdays with row and column total in dynamic pivot

I have a table in which I have some Employee details like id,employeeid,workdate,taskid,hours,entrydate,entryby
And the other table have the basic information about user like firstname,lastname,emailid,password
now I want to create a crosstab query in which I want to display username and the weekday and the working total hours of the employee.
well first i was thinking to use temp table for this But I failed to do so I use something like this for each day of week
select distinct employeeid ,sum(hours) as TotalHours ,'MONDAY' as Day into #Monday from Project_TimeSheet where year(entrydate)='2014' and month(entrydate)='12' and datename(dw,entrydate)='MONDAY' group by employeeid
But for me this is not working. Anybody please tell me the query for this using pivot I want the result something like this
SELECT employeeid,
SUM(CASE WHEN datename(dw,entrydate)='MONDAY' THEN hours END) as Monday,
SUM(CASE WHEN datename(dw,entrydate)='TUESDAY' THEN hours END) as Tuesday,
.....,
sum(hours) as TotalHours
FROM Project_TimeSheet
WHERE year(entrydate)='2014' and month(entrydate)='12'
GROUP BY employeeid
WITH ROLLUP
Here is the sample table
and the sample code
CREATE TABLE #TEMP (Name varchar(10), [DATE] datetime,
TotalHours int)
INSERT #TEMP VALUES
('A','01/JAN/2014',10),
('B','02/JAN/2014',20),
('A','03/JAN/2014',20),
('B','04/JAN/2014',30),
('A','05/JAN/2014',40),
('B','06/JAN/2014',50),
('A','07/JAN/2014',60),
('A','08/JAN/2014',65),
('Z','07/JAN/2014',72),
('B','15/FEB/2014',70),
('B','16/FEB/2014',50),
('A','17/FEB/2014',60),
('B','18/FEB/2014',70)
Set monday as your First day of the week in Sql Server
SET DATEFIRST 1;
Insert data into a new table to order the weekedays and bring total as the last column
SELECT DISTINCT DATENAME(WEEKDAY,[DATE])WK
,DATEPART(DW,[DATE]) WDNO
INTO #ORDERTABLE
FROM #TEMP
UNION ALL
SELECT 'TOTAL HOURS',8
ORDER BY 2,1
Now we do the logic for calculating the total for each Name and Weekdays and bring row-Total in the last row.
SELECT
CASE Name WHEN 'TOTAL BY DAY' THEN 0
ELSE DENSE_RANK() OVER(ORDER BY NAME DESC)
END RNO,*
INTO #NEWTABLE
FROM
(
SELECT CASE WHEN Name IS NULL THEN 'TOTAL BY DAY' ELSE Name END Name,
CASE WHEN DATENAME(WEEKDAY,[DATE]) IS NULL THEN 'TOTAL HOURS' ELSE DATENAME(WEEKDAY,[DATE]) END WK,
SUM(TotalHours)TotalHours
FROM #TEMP
WHERE YEAR([DATE])=2014 AND DATENAME(MONTH,[DATE])='JANUARY'
GROUP BY Name,DATENAME(WEEKDAY,[DATE])
WITH CUBE
)TAB
ORDER BY RNO DESC
Select the distinct columns from the #ORDERTABLE table
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + WK + ']',
'[' + WK + ']')
FROM (SELECT DISTINCT WK,WDNO FROM #ORDERTABLE) PV
ORDER BY WDNO
Now pivot the query and order by RNO(in which we have applied logic to bring Total in the last row)
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT NAME,' + #cols + ' FROM
(
SELECT * FROM #NEWTABLE
) x
PIVOT
(
SUM(TotalHours)
FOR WK IN (' + #cols + ')
) p
ORDER BY RNO DESC
'
EXEC SP_EXECUTESQL #query
and here is the result
Here is the SQLFIDDLE http://sqlfiddle.com/#!3/655df/6
(If it shows any error on loading just click RUNSQL button, it will work)
Here is an update where we need to find Startdate and EndDate of that week for a given date(Add the below just after the insertion into #TEMP table)
DECLARE #STARTDATE DATE;
DECLARE #ENDDATE DATE;
SELECT
#STARTDATE = CASE WHEN DATENAME(WEEKDAY,[DATE])='MONDAY' THEN [DATE]
WHEN DATENAME(WEEKDAY,[DATE])='SUNDAY' THEN DATEADD(DAY,-6,[DATE])
ELSE DATEADD(DAY,-datepart(dw,[DATE])+1,[DATE]) END
,#ENDDATE = CASE WHEN DATENAME(WEEKDAY,[DATE])='MONDAY' THEN DATEADD(DAY,6,[DATE])
WHEN DATENAME(WEEKDAY,[DATE])='SUNDAY' THEN [DATE]
ELSE DATEADD(DAY,-datepart(dw,[DATE])+7,[DATE]) END
FROM #TEMP
WHERE [DATE]=CAST('2014-01-06' AS DATE)
And use the above variables in the WHERE condition(before the CUBE) ie,
WHERE [DATE] BETWEEN #STARTDATE AND #ENDDATE
try like,
Declare #Year varchar(4)=2014
Declare #Month varchar(2)=12
Declare #Input datetime=#Year+'/'+#Month+'/'+'01'
select #input
select employeeid
,(Select sum(hours) as TotalHours from Project_TimeSheet M where year(entrydate)='2014'
and month(entrydate)='12' and datename(dw,entrydate)='MONDAY' and m.employeeid=pts.employeeid )'MONDAY'
from Project_TimeSheet PTS
,(Select sum(hours) as TotalHours from Project_TimeSheet M where year(entrydate)='2014'
and month(entrydate)='12' and datename(dw,entrydate)='TUESDAY' and m.employeeid=pts.employeeid )'TUESDAY'
.....,
from Project_TimeSheet PTS
where year(entrydate)='2014' and month(entrydate)='12' and datename(dw,entrydate)='MONDAY'
group by employeeid
OR
select
SUM(MONDAY)MONDAY,SUM(TUESDAY)TUESDAY
,........
sum(hours)TotalHours
from
(select employeeid
,case WHEN datename(dw,entrydate)='MONDAY' THEN hours else 0 end 'MONDAY'
,case WHEN datename(dw,entrydate)='TUESDAY' THEN hours else 0 end 'TUESDAY'
,.....
,hours
from Project_TimeSheet PTS
where year(entrydate)='2014' and month(entrydate)='12' and datename(dw,entrydate)='MONDAY'
)tbl
group by employeeid

Resources