Excluding rows derived from case statement with a specific value based on values from another column - sql

How do I exclude rows with a specific value in a field based on specific values of another field? To dive deeper:
I have a column called "rule_name" that's derived from a case statement.
I have another column called "DeliveryTermsIdentifier" that's pulled from another table (see image of query result below).
I'm trying to remove rows with rule_name "Delivery event required" only if DeliveryTermsIdentifier is "PP", "DA", ect. I'm getting thrown off because my rule_name column is based off a condition already.
image of query result
Here is the SQL used to derive those 2 columns:
,case when s.ShipmentNo is not null then cast(dqr.rulename as varchar(30))
else cast(dqc.dataqualityruleid as varchar(30)) end as rule_name
,case when (dqd.SuccessFlag = 0) then 'Missing'
when (dqd.SuccessFlag = 1) then 'Reported'
when (s.ShipmentNo is null) then 'Missing' end reporting_result
,s.DeliveryTermsIdentifier
from CustomerMasterList cml
join organization o on o.organizationid = cml.senderid
inner join DataQualityConfig dqc on dqc.DomainId = cml.DomainID
left outer join shipment s on s.shipmentno = cml.shipmentno
left outer join dataqualityresult dqd on dqd.shipmentid = s.shipmentid
left outer join dataqualityrule dqr on dqr.dataqualityruleid
=dqd.dataqualityruleid

One way is to put what you have in a subquery and filter out with a where clause in the outer query. E.g.
SELECT rule_name, reporting_result, DeliveryTermsIdentifier
FROM
(SELECT 'X'
,case when s.ShipmentNo is not null then cast(dqr.rulename as varchar(30))
else cast(dqc.dataqualityruleid as varchar(30)) end as rule_name
,case when (dqd.SuccessFlag = 0) then 'Missing'
when (dqd.SuccessFlag = 1) then 'Reported'
when (s.ShipmentNo is null) then 'Missing' end reporting_result
,s.DeliveryTermsIdentifier
from CustomerMasterList cml
join organization o on o.organizationid = cml.senderid
inner join DataQualityConfig dqc on dqc.DomainId = cml.DomainID
left outer join shipment s on s.shipmentno = cml.shipmentno
left outer join dataqualityresult dqd on dqd.shipmentid = s.shipmentid
left outer join dataqualityrule dqr on dqr.dataqualityruleid
=dqd.dataqualityruleid ) AS sub
WHERE DeliveryTermsIdentifier NOT IN ('PP', 'DA') AND rule_name <> 'Delivery event required'

You can also encapsulate your case statements into an outer apply and use the alias like this:
SELECT * ,
rules.rule_name ,
RESULT.reporting_result ,
s.DeliveryTermsIdentifier
FROM CustomerMasterList cml
JOIN organization o ON o.organizationid = cml.senderid
INNER JOIN DataQualityConfig dqc ON dqc.DomainId = cml.DomainID
LEFT OUTER JOIN shipment s ON s.shipmentno = cml.shipmentno
LEFT OUTER JOIN dataqualityresult dqd ON dqd.shipmentid = s.shipmentid
LEFT OUTER JOIN dataqualityrule dqr ON dqr.dataqualityruleid = dqd.dataqualityruleid
OUTER APPLY ( SELECT CASE WHEN ( dqd.SuccessFlag = 0 ) THEN 'Missing'
WHEN ( dqd.SuccessFlag = 1 ) THEN 'Reported'
WHEN ( s.ShipmentNo IS NULL ) THEN 'Missing'
END reporting_result ) RESULT
OUTER APPLY ( SELECT CASE WHEN s.ShipmentNo IS NOT NULL THEN
CAST(dqr.rulename AS VARCHAR(30))
ELSE
CAST(dqc.dataqualityruleid AS VARCHAR(30))
END AS rule_name ) rules
WHERE s.DeliveryTermsIdentifier NOT IN ( 'PP', 'DA' )
AND rules.rule_name <> 'Delivery event required';

Related

Where Clause Using Conditional Statement

i have query below
SELECT #RoleUser = MR.Code FROM MasterRole MR INNER JOIN MasterUsersRole MUR ON MR.Id = MUR.RoleId
INNER JOIN MasterUsers MU ON Mu.UserCode = MUR.UserCode
WHERE MU.UserCode = #UserLoginID
select 1 Num
, MyHistory.ID
, MyHistory.RequestNumber
, MyHistory.FlowID
, MyHistory.FlowProcessStatusID
from
(
select *
from Requests R
inner join
(
--DECLARE #UserLoginID nvarchar(200) = 'dum.testing.3'
select distinct
RequestID
from dbo.RequestTrackingHistory RTH
where IIF(#UserLoginID = 'admin', #UserLoginID, RTH.CreatedBy) = #UserLoginID
OR ( CreatedBy IN
SELECT Mu.UserCode from MasterUsers MU
INNER JOIN MasterUsersRole MUR ON MU.UserCode = MUR.UserCode
INNER JOIN MasterRole MR ON MUR.RoleId = MR.Id
WHERE MR.Code = #RoleUser
)
)
) RT on R.ID = RT.RequestID
) as MyHistory
inner join MasterFlow F on MyHistory.FlowID = F.ID
inner join
(
select FP.ID
, FP.Name
, FP.AssignType
, FP.AssignTo
, FP.IsStart
, case FP.AssignType
when 'GROUP' then
G.Name
end as 'AssignToName'
from MasterFlowProcess FP
left join dbo.MasterRole G on FP.AssignTo = G.ID and FP.AssignType = 'GROUP'
) FP on MyHistory.FlowProcessID = FP.ID
inner join MasterFlowProcessStatus FPS on MyHistory.FlowProcessStatusID = FPS.ID
left join MasterFlowProcessStatusNext FPSN on FPS.ID = FPSN.ProcessStatusFlowID
left join MasterFlowProcess FPN on FPSN.NextProcessFlowID = FPN.ID
left JOIN MasterRole MR ON MR.Id = FPN.AssignTo
left join MasterUsersRole MUR on MR.Id = MUR.RoleId
left join MasterUsers MURO on MUR.UserCode = MURO.UserCode
inner join MasterUsers UC on MyHistory.CreatedBy = UC.UserCode
left join MasterUsers UU on MyHistory.UpdatedBy = UU.UserCode
LEFT JOIN RequestMT RMT ON MyHistory.ID = RMT.RequestID
LEFT JOIN RequestGT RGT ON MyHistory.ID = RGT.RequestID
left join (SELECT sum(QtyCU) countQty , RequestId from dbo.RequestGTDetail where IsActive = 1 group by RequestId) RGTD on RGTD.RequestId = RGT.RequestId
left join (SELECT sum(QtyPCS) countQty , RequestId from dbo.RequestMTDetail where IsActive = 1 group by RequestId) RMTD on RMTD.RequestId = RMT.RequestId
left join (SELECT COUNT(IIF(returnable = 0, returnable, null)) countReturnable , RequestId from dbo.RequestMTDetail group by RequestId) RMTR on RMTR.RequestId = RMT.RequestId
left JOIN dbo.MasterDistributor md ON md.Code = RGT.CustId or md.Code = RMT.CustId
left JOIN dbo.MasterUsersDistributor MUD ON MUD.UserCode = MURO.UserCode AND md.Code = MUD.DistributorCode
LEFT JOIN dbo.MasterReason MRMT ON RMT.ReasonId = MRMT.Id
LEFT JOIN dbo.MasterReason MRGT ON RGT.ReasonId = MRGT.Id
LEFT JOIN dbo.MasterDistributorGroup MDG ON MDG.Id = MD.GroupId
OUTER APPLY dbo.FnGetHistoryApproveDate(MyHistory.Id) AS x
where REPLACE(FPS.Name, '#Requestor', uc.Name) <> 'DRAFT'
AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
i want to add some logic in where clause
this line
==> AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
it depend on the #RoleUser variable, if #RoleUser IN ('A','B') then where clause above is executed, but if #RoleUser Not IN ('A','B') where clause not executed
i,m trying this where clause
AND IIF(#RoleUser IN ('A','B'), MUD.DistributorCode, #RoleUser) IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = IIF(#RoleUser IN ('A','B'), #UserLoginID, NULL))
it didn't work, only executed if #RoleUser IS ('A','B') other than that it return 0 record
any help or advice is really appreciated
thank you
The cleanest way I'm implemented these kind of crazy rules is a
holderTable
and a countVariable against the holder table.
I'll give a generic examples.
This is a "approach" and "philosophy", not a specific answer....with complex WHERE clauses.
DECLARE #customerCountryCount int
DECLARE #customerCountry TABLE ( CountryName varchar(15) )
if ( "the moon is blue on tuesday" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Honduras" UNION ALL SELECT "Malaysia"
END
if ( "favorite color = green" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Greenland" UNION ALL SELECT "Peru"
END
SELECT #customerCountryCount = COUNT(*) FROM #customerCountry
Select * from dbo.Customers c
WHERE
(#customerCountryCount = 0)
OR
( exists (select null from #customerCountry innerVariableTable where UPPER(innerVariableTable.CountryName) = UPPER(c.Country) ))
)
This way, instead of putting all the "twisted logic" in an overly complex WHERE statement..... you have "separation of concerns"...
Your inserts into #customerCountry are separated from your use of #customerCountry.
And you have the #customerCountryCount "trick" to distinguish when nothing was used.
You can add a #customerCountryNotExists table as well, and code it to where not exists.
As a side note, you may want to try using a #temp table (instead of a #variabletable (#customerCountry above)... and performance test these 2 options.
There is no "single answer". You have to "try it out".
And many many variables go into #temp table performance (from a sql-server SETUP, not "how you code a stored procedure". That is way outside the scope of this question.
Here is a SOF link to "safe" #temp table usage.
Temporary table in SQL server causing ' There is already an object named' error

LEFT OUTER JOIN WITH inner join with case in SQL DBMS

I have the following query
SELECT
A.WORKNR AS "WORK-nr",
UCASE(A.NAME) AS "NAME" ,
UCASE(A.ADRESS) AS "adress",
A.ZIPNR,
UCASE(A.ZIPADR) AS "zipadress",
A.NNR,
CASE
WHEN AO.ORG_REF IS NULL THEN 'OK'
ELSE 'NO '
END AS RESULT OF ORG ,
CASE
WHEN B.B = 1 THEN 'OK'
ELSE 'COME WITH ANOTHER NNR'
END AS COMPARE_RESULT
FROM
WORK.WORK_ADRESS A
LEFT JOIN
(SELECT
POSTNR, UCASE(STREET) AS STREET,
COUNT(DISTINCT NNR) AS B
FROM
WORK.WORK_ADRESS
GROUP BY
ZIPNR, UCASE(STREET)) B ON B.STREET = UCASE(A.STREET)
AND B.ZIPNR = ZIPNR
LEFT JOIN
(SELECT *
FROM WORK.ORGANISATION2) AO ON AO.WORK_REF = A.WORK_REF
AND AO.TILL >= CURRENT DATE
what I want is to add a JOIN to a third table ORG3 that contains the name for org genom
SELECT WORK.ORGANISATION2
INNER JOIN STYR.ORG2 ORG3 ON AO.ORG_REF3 = ORG3.ORG_REF
and get the column OR3.NAME as a separate column.
Can anyone help me? I am thankful

SQL Server 2008 : FROM clause syntax error

My code returns a syntax error on L42 (the from clause). I've looked and looked and to me, the syntax looks correct for the FROM.
The top part of the code creates a temp table 'subproducts'. Then I want to pull data from DB tables & pull relevant details from the new temp table.
Any ideas?
drop table ##subproducts;
SELECT
F1.[OrderNo]
,F1.[OrderSeqNo]
,F1.[OrderLineNo]
,sum(F1.[LineCost]) as LineCost
,sum(F1.[NetCost]) as NetCost
,sum(F1.[OrderNet]) as OrderNet
INTO
##subproducts
FROM
[UFPData].[dbo].[SalesStats] F1
WHERE
F1.MainProd = 'S'
GROUP BY
F1.OrderNo, F1.OrderSeqNo, F1.OrderLineNo;
SELECT *
FROM SalesStats F1
LEFT OUTER JOIN ##subproducts F2 ON F1.OrderNo = F2.OrderNo
AND F1.OrderSeqNo = F2.Orderseqno
AND F1.OrderLineNo = F2.OrderLineNo
WHERE YEAR(InvDate) = 2015
AND MONTH(InvDate) = 5
AND CustNo = 100382
AND MainProd = 'Y';
SELECT
ContractInfo.CONTRNO,
ContractInfo.CONTRDESC,
repcode,
RepName,
PriceAgreement,
ordercycle,
SalesStats.CustNo,
Customer.CustName,
SalesStats.InvDate,
SalesStats.InvoiceNo,
salesstats.ProdCode,
Price as ContractPrice,
subproduct.LineCost as LineCost,
subproduct.NetCost as NetCost,
subproduct.OrderNet as OrderNet,
contractinfo.NETCOST as BidPrice,
contractinfo.NETCOST* SUM(quantity) as BidCost,
FROM
UFPData.dbo.SalesStats
RIGHT OUTER JOIN
##subproducts ON UFPData.dbo.SalesStats.ProdCode = ##subproducts.ProdCode,
UFPData.dbo.SalesStats
LEFT OUTER JOIN
UFPData.dbo.Customer ON UFPData.dbo.SalesStats.CustNo = UFPData.dbo.Customer.CustNo, UFPData.dbo.SalesStats
LEFT OUTER JOIN
Pricing.dbo.InvManCen ON UFPData.dbo.SalesStats.ProdCode = Pricing.dbo.invmancen.UFPCODE, UFPData.dbo.SalesStats
RIGHT OUTER JOIN
UFPData.dbo.ContractInfo ON UFPData.dbo.Customer.CustNo = UFPData.dbo.ContractInfo.CUSTNO
AND UFPData.dbo.salesstats.prodcode = UFPData.dbo.contractinfo.prodcode,
WHERE
invdate BETWEEN '2015-05-01' and '2015-05-31'
AND TeamCode IN ('tm1','tm2','tm3')
AND ContractInfo.CONTRNO IN ('1500','1502','1503','1504','1505','1506','701','702','703','705','141',
'712', '713', '714', '715', '716', '717', '718', '719', '730', '731', '732') or CONTRNO between '3000' and '3049')
--and left(ordercycle,1) <> 'c'
GROUP BY
ContractInfo.CONTRNO,
repcode,
RepName,
SalesStats.CustNo,
Customer.CustName,
SalesStats.InvDate,
salesstats.ProdCode,
Price,
contractinfo.NETCOST,
SalesStats.InvoiceNo,
InvManCen.PS98,
ContractInfo.CONTRDESC,
ordercycle,
PriceAgreement,
case
when invdate > ('20' + LEFT(ENDDATE,2)+ '-'+ SUBSTRING(cast(ENDDATE as varchar(6)),3,2)+ '-'+ SUBSTRING(cast(ENDDATE as varchar(6)),5,2)) then 'Expired' else 'Live' end;
Many thanks in advance
This doesn't look right to me:
FROM
UFPData.dbo.SalesStats right outer join ##subproducts on UFPData.dbo.SalesStats.ProdCode = ##subproducts.ProdCode,
UFPData.dbo.SalesStats left outer join UFPData.dbo.Customer on UFPData.dbo.SalesStats.CustNo = UFPData.dbo.Customer.CustNo,
UFPData.dbo.SalesStats left outer join Pricing.dbo.InvManCen on UFPData.dbo.SalesStats.ProdCode = Pricing.dbo.invmancen.UFPCODE,
UFPData.dbo.SalesStats right outer join UFPData.dbo.ContractInfo on UFPData.dbo.Customer.CustNo = UFPData.dbo.ContractInfo.CUSTNO and UFPData.dbo.salesstats.prodcode = UFPData.dbo.contractinfo.prodcode,
Usually the format is something like this:
FROM
TableName
INNER JOIN SomeOtherTable...
INNER JOIN AnotherTable...
Note that the first table is mentioned once, not before every join. You also seem to have errant commas after each join, which isn't syntactically correct. I think you want this:
FROM
UFPData.dbo.SalesStats
right outer join ##subproducts on UFPData.dbo.SalesStats.ProdCode = ##subproducts.ProdCode
left outer join UFPData.dbo.Customer on UFPData.dbo.SalesStats.CustNo = UFPData.dbo.Customer.CustNo
left outer join Pricing.dbo.InvManCen on UFPData.dbo.SalesStats.ProdCode = Pricing.dbo.invmancen.UFPCODE
right outer join UFPData.dbo.ContractInfo on UFPData.dbo.Customer.CustNo = UFPData.dbo.ContractInfo.CUSTNO and UFPData.dbo.salesstats.prodcode = UFPData.dbo.contractinfo.prodcode
There could very well be more syntax errors throughout your query. I recommend formatting the code a little more cleanly to more easily find them. For example, contrast the above with something like this:
FROM
UFPData.dbo.SalesStats
RIGHT OUTER JOIN ##subproducts
ON UFPData.dbo.SalesStats.ProdCode = ##subproducts.ProdCode
LEFT OUTER JOIN UFPData.dbo.Customer
ON UFPData.dbo.SalesStats.CustNo = UFPData.dbo.Customer.CustNo
LEFT OUTER JOIN Pricing.dbo.InvManCen
ON UFPData.dbo.SalesStats.ProdCode = Pricing.dbo.invmancen.UFPCODE
RIGHT OUTER JOIN UFPData.dbo.ContractInfo
ON UFPData.dbo.Customer.CustNo = UFPData.dbo.ContractInfo.CUSTNO
AND UFPData.dbo.salesstats.prodcode = UFPData.dbo.contractinfo.prodcode
It's a small change, and often a matter of personal preference, but well formatted code brings about a number of advantages. Less horizontal scrolling makes it easier to see the relevant code in a single glance, line breaks for specific keywords and clauses makes it easier to spot errant commas, etc.
Here's what I think should be valid syntax for what you've provided, please try it and compare it to the original as there were multiple errors:
DROP TABLE ##subproducts;
SELECT F1.[OrderNo] ,
F1.[OrderSeqNo] ,
F1.[OrderLineNo] ,
SUM(F1.[LineCost]) AS LineCost ,
SUM(F1.[NetCost]) AS NetCost ,
SUM(F1.[OrderNet]) AS OrderNet
INTO ##subproducts
FROM [UFPData].[dbo].[SalesStats] F1
WHERE F1.MainProd = 'S'
GROUP BY F1.OrderNo ,
F1.OrderSeqNo ,
F1.OrderLineNo;
SELECT *
FROM SalesStats F1
LEFT OUTER JOIN ##subproducts F2 ON F1.OrderNo = F2.OrderNo
AND F1.OrderSeqNo = F2.Orderseqno
AND F1.OrderLineNo = F2.OrderLineNo
WHERE YEAR(InvDate) = 2015
AND MONTH(InvDate) = 5
AND CustNo = 100382
AND MainProd = 'Y';
SELECT ContractInfo.CONTRNO ,
ContractInfo.CONTRDESC ,
repcode ,
RepName ,
PriceAgreement ,
ordercycle ,
SalesStats.CustNo ,
Customer.CustName ,
SalesStats.InvDate ,
SalesStats.InvoiceNo ,
SalesStats.ProdCode ,
Price AS ContractPrice ,
subproduct.LineCost AS LineCost ,
subproduct.NetCost AS NetCost ,
subproduct.OrderNet AS OrderNet ,
ContractInfo.NetCost AS BidPrice ,
ContractInfo.NetCost * SUM(quantity) AS BidCost
FROM UFPData.dbo.SalesStats
RIGHT OUTER JOIN ##subproducts ON UFPData.dbo.SalesStats.ProdCode = ##subproducts.ProdCode
LEFT OUTER JOIN UFPData.dbo.Customer ON UFPData.dbo.SalesStats.CustNo = UFPData.dbo.Customer.CustNo
LEFT OUTER JOIN Pricing.dbo.InvManCen ON UFPData.dbo.SalesStats.ProdCode = Pricing.dbo.invmancen.UFPCODE
RIGHT OUTER JOIN UFPData.dbo.ContractInfo ON UFPData.dbo.Customer.CustNo = UFPData.dbo.ContractInfo.CUSTNO
AND UFPData.dbo.salesstats.prodcode = UFPData.dbo.contractinfo.prodcode
WHERE invdate BETWEEN '2015-05-01' AND '2015-05-31'
AND TeamCode IN ( 'tm1', 'tm2', 'tm3' )
AND (ContractInfo.CONTRNO IN ( '1500', '1502', '1503', '1504', '1505',
'1506', '701', '702', '703', '705',
'141', '712', '713', '714', '715', '716',
'717', '718', '719', '730', '731', '732' )
OR CONTRNO BETWEEN '3000' AND '3049')
GROUP BY ContractInfo.CONTRNO ,
repcode ,
RepName ,
SalesStats.CustNo ,
Customer.CustName ,
SalesStats.InvDate ,
SalesStats.ProdCode ,
Price ,
ContractInfo.NetCost ,
SalesStats.InvoiceNo ,
InvManCen.PS98 ,
ContractInfo.CONTRDESC ,
ordercycle ,
PriceAgreement ,
CASE WHEN invdate > ( '20' + LEFT(ENDDATE, 2) + '-'
+ SUBSTRING(CAST(ENDDATE AS VARCHAR(6)), 3, 2)
+ '-' + SUBSTRING(CAST(ENDDATE AS VARCHAR(6)), 5,
2) ) THEN 'Expired'
ELSE 'Live'
END;

SQL-subquery workaround to join based on alias

Attempted Subquery using alias
SELECT
*,
IQ.EndPart,
IQ.QtyToShip
FROM
parts p
INNER JOIN (
select
*,
(case when c.kitno is null then l.partno else c.partno end) as [EndPart],
(case when c.kitno is null then l.TotalQuantity else c.reqqty end) as [QtyToShip]
FROM
shipments s
INNER JOIN shipments_li l ON s.ShipmentNo = l.ShipmentNo
LEFT JOIN ProductConfiguration c ON l.PartNo = c.KitNo WHERE s.Status='N' and year(s.OrderDate)>2007
) IQ ON p.partno = IQ.EndPart
Looking for a way to join the parts table to my query below, using the part # which is aliased as EndPart. If there is another way to acheive the results of taking two columns and combining them instead of case and an alias that would be a great alternative as well. All my searches of other individuals quest to achieve the same have yielded the result you cannot perform a join based on alias because the results have not been determined at that point, recommending a subquery as a workaround. I'm just not sure how to acheive working results. The above query was what I attempted to get working. Any help appreciated. Thanks in advance.
Original Query
SELECT
*,
(case when c.kitno is null then l.partno else c.partno end) as [EndPart],
(case when c.kitno is null then l.TotalQuantity else c.reqqty end) as [QtyToShip]
FROM
shipments s
INNER JOIN shipments_li l ON s.ShipmentNo = l.ShipmentNo
LEFT JOIN ProductConfiguration c ON l.PartNo = c.KitNo
WHERE
s.Status='N'
and year(s.OrderDate)>2007
order by s.shipmentno
More proper way
--- CREATE TempTable
DECLARE #tblTemp AS TABLE (EndPart INT,
QtyToShip INT)
INSERT INTO #tblTemp (EndPart, QtyToShip)
SELECT
(CASE WHEN c.kitno IS NULL
THEN l.partno ELSE c.partno END) AS [EndPart],
(CASE WHEN c.kitno IS NULL
THEN l.TotalQuantity
ELSE c.reqqty END) AS [QtyToShip]
FROM dbo.Shipments s WITH(NOLOCK)
INNER JOIN shipments_li l WITH(NOLOCK) ON s.ShipmentNo = l.ShipmentNo
LEFT JOIN ProductConfiguration c WITH(NOLOCK) ON l.PartNo = c.KitNo
WHERE s.Status='N' AND YEAR(s.OrderDate)>2007
SELECT
p.*,
tmp.EndPart,
tmp.QtyToShip
FROM dbo.parts p WITH(NOLOCK)
INNER JOIN #tblTemp tmp ON p.partno = tmp.EndPart

TSQL Conditionally Select Specific Value

This is a follow-up to #1644748 where I successfully answered my own question, but Quassnoi helped me to realize that it was the wrong question. He gave me a solution that worked for my sample data, but I couldn't plug it back into the parent stored procedure because I fail at SQL 2005 syntax. So here is an attempt to paint the broader picture and ask what I actually need.
This is part of a stored procedure that returns a list of items in a bug tracking application I've inherited. There are are over 100 fields and 26 joins so I'm pulling out only the mostly relevant bits.
SELECT
tickets.ticketid,
tickets.tickettype,
tickets_tickettype_lu.tickettypedesc,
tickets.stage,
tickets.position,
tickets.sponsor,
tickets.dev,
tickets.qa,
DATEDIFF(DAY, ticket_history_assignment.savedate, GETDATE()) as 'daysinqueue'
FROM
dbo.tickets WITH (NOLOCK)
LEFT OUTER JOIN dbo.tickets_tickettype_lu WITH (NOLOCK) ON tickets.tickettype = tickets_tickettype_lu.tickettypeid
LEFT OUTER JOIN dbo.tickets_history_assignment WITH (NOLOCK) ON tickets_history_assignment.ticketid = tickets.ticketid
AND tickets_history_assignment.historyid = (
SELECT
MAX(historyid)
FROM
dbo.tickets_history_assignment WITH (NOLOCK)
WHERE
tickets_history_assignment.ticketid = tickets.ticketid
GROUP BY
tickets_history_assignment.ticketid
)
WHERE
tickets.sponsor = #sponsor
The area of interest is the daysinqueue subquery mess. The tickets_history_assignment table looks roughly as follows
declare #tickets_history_assignment table (
historyid int,
ticketid int,
sponsor int,
dev int,
qa int,
savedate datetime
)
insert into #tickets_history_assignment values (1521402, 92774,20,14, 20, '2009-10-27 09:17:59.527')
insert into #tickets_history_assignment values (1521399, 92774,20,14, 42, '2009-08-31 12:07:52.917')
insert into #tickets_history_assignment values (1521311, 92774,100,14, 42, '2008-12-08 16:15:49.887')
insert into #tickets_history_assignment values (1521336, 92774,100,14, 42, '2009-01-16 14:27:43.577')
Whenever a ticket is saved, the current values for sponsor, dev and qa are stored in the tickets_history_assignment table with the ticketid and a timestamp. So it is possible for someone to change the value for qa, but leave sponsor alone.
What I want to know, based on all of these conditions, is the historyid of the record in the tickets_history_assignment table where the sponsor value was last changed so that I can calculate the value for daysinqueue. If a record is inserted into the history table, and only the qa value has changed, I don't want that record. So simply relying on MAX(historyid) won't work for me.
Quassnoi came up with the following which seemed to work with my sample data, but I can't plug it into the larger query, SQL Manager bitches about the WITH statement.
;WITH rows AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ticketid ORDER BY savedate DESC) AS rn
FROM #Table
)
SELECT rl.sponsor, ro.savedate
FROM rows rl
CROSS APPLY
(
SELECT TOP 1 rc.savedate
FROM rows rc
JOIN rows rn
ON rn.ticketid = rc.ticketid
AND rn.rn = rc.rn + 1
AND rn.sponsor <> rc.sponsor
WHERE rc.ticketid = rl.ticketid
ORDER BY
rc.rn
) ro
WHERE rl.rn = 1
I played with it yesterday afternoon and got nowhere because I don't fundamentally understand what is going on here and how it should fit into the larger context.
So, any takers?
UPDATE
Ok, here's the whole thing. I've been switching some of the table and column names in an attempt to simplify things so here's the full unedited mess.
snip - old bad code
Here are the errors:
Msg 102, Level 15, State 1, Procedure usp_GetProjectRecordsByAssignment, Line 159
Incorrect syntax near ';'.
Msg 102, Level 15, State 1, Procedure usp_GetProjectRecordsByAssignment, Line 179
Incorrect syntax near ')'.
Line numbers are of course not correct but refer to
;WITH rows AS
And the ')' char after the WHERE rl.rn = 1
)
Respectively
Is there a tag for extra super long question?
UPDATE #2
Here is the finished query for anyone who may need this:
CREATE PROCEDURE [dbo].[usp_GetProjectRecordsByAssignment]
(
#assigned numeric(18,0),
#assignedtype numeric(18,0)
)
AS
SET NOCOUNT ON
WITH rows AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY recordid ORDER BY savedate DESC) AS rn
FROM projects_history_assignment
)
SELECT projects_records.recordid,
projects_records.recordtype,
projects_recordtype_lu.recordtypedesc,
projects_records.stage,
projects_stage_lu.stagedesc,
projects_records.position,
projects_position_lu.positiondesc,
CASE projects_records.clientrequested
WHEN '1' THEN 'Yes'
WHEN '0' THEN 'No'
END AS clientrequested,
projects_records.reportingmethod,
projects_reportingmethod_lu.reportingmethoddesc,
projects_records.clientaccess,
projects_clientaccess_lu.clientaccessdesc,
projects_records.clientnumber,
projects_records.project,
projects_lu.projectdesc,
projects_records.version,
projects_version_lu.versiondesc,
projects_records.projectedversion,
projects_version_lu_projected.versiondesc AS projectedversiondesc,
projects_records.sitetype,
projects_sitetype_lu.sitetypedesc,
projects_records.title,
projects_records.module,
projects_module_lu.moduledesc,
projects_records.component,
projects_component_lu.componentdesc,
projects_records.loginusername,
projects_records.loginpassword,
projects_records.assistedusername,
projects_records.browsername,
projects_browsername_lu.browsernamedesc,
projects_records.browserversion,
projects_records.osname,
projects_osname_lu.osnamedesc,
projects_records.osversion,
projects_records.errortype,
projects_errortype_lu.errortypedesc,
projects_records.gsipriority,
projects_gsipriority_lu.gsiprioritydesc,
projects_records.clientpriority,
projects_clientpriority_lu.clientprioritydesc,
projects_records.scheduledstartdate,
projects_records.scheduledcompletiondate,
projects_records.projectedhours,
projects_records.actualstartdate,
projects_records.actualcompletiondate,
projects_records.actualhours,
CASE projects_records.billclient
WHEN '1' THEN 'Yes'
WHEN '0' THEN 'No'
END AS billclient,
projects_records.billamount,
projects_records.status,
projects_status_lu.statusdesc,
CASE CAST(projects_records.assigned AS VARCHAR(5))
WHEN '0' THEN 'N/A'
WHEN '10000' THEN 'Unassigned'
WHEN '20000' THEN 'Client'
WHEN '30000' THEN 'Tech Support'
WHEN '40000' THEN 'LMI Tech Support'
WHEN '50000' THEN 'Upload'
WHEN '60000' THEN 'Spider'
WHEN '70000' THEN 'DB Admin'
ELSE rtrim(users_assigned.nickname) + ' ' + rtrim(users_assigned.lastname)
END AS assigned,
CASE CAST(projects_records.assigneddev AS VARCHAR(5))
WHEN '0' THEN 'N/A'
WHEN '10000' THEN 'Unassigned'
ELSE rtrim(users_assigneddev.nickname) + ' ' + rtrim(users_assigneddev.lastname)
END AS assigneddev,
CASE CAST(projects_records.assignedqa AS VARCHAR(5))
WHEN '0' THEN 'N/A'
WHEN '10000' THEN 'Unassigned'
ELSE rtrim(users_assignedqa.nickname) + ' ' + rtrim(users_assignedqa.lastname)
END AS assignedqa,
CASE CAST(projects_records.assignedsponsor AS VARCHAR(5))
WHEN '0' THEN 'N/A'
WHEN '10000' THEN 'Unassigned'
ELSE rtrim(users_assignedsponsor.nickname) + ' ' + rtrim(users_assignedsponsor.lastname)
END AS assignedsponsor,
projects_records.clientcreated,
CASE projects_records.clientcreated
WHEN '1' THEN 'Yes'
WHEN '0' THEN 'No'
END AS clientcreateddesc,
CASE projects_records.clientcreated
WHEN '1' THEN rtrim(clientusers_createuser.firstname) + ' ' + rtrim(clientusers_createuser.lastname) + ' (Client)'
ELSE rtrim(users_createuser.nickname) + ' ' + rtrim(users_createuser.lastname)
END AS createuser,
projects_records.createdate,
projects_records.savedate,
projects_resolution.sitesaffected,
projects_sitesaffected_lu.sitesaffecteddesc,
DATEDIFF(DAY, projects_history_assignment.savedate, GETDATE()) as 'daysinqueue',
projects_records.iOnHitList,
projects_records.changetype
FROM
dbo.projects_records WITH (NOLOCK)
LEFT OUTER JOIN dbo.projects_recordtype_lu WITH (NOLOCK) ON projects_records.recordtype = projects_recordtype_lu.recordtypeid
LEFT OUTER JOIN dbo.projects_stage_lu WITH (NOLOCK) ON projects_records.stage = projects_stage_lu.stageid
LEFT OUTER JOIN dbo.projects_position_lu WITH (NOLOCK) ON projects_records.position = projects_position_lu.positionid
LEFT OUTER JOIN dbo.projects_reportingmethod_lu WITH (NOLOCK) ON projects_records.reportingmethod = projects_reportingmethod_lu.reportingmethodid
LEFT OUTER JOIN dbo.projects_lu WITH (NOLOCK) ON projects_records.project = projects_lu.projectid
LEFT OUTER JOIN dbo.projects_version_lu WITH (NOLOCK) ON projects_records.version = projects_version_lu.versionid
LEFT OUTER JOIN dbo.projects_version_lu projects_version_lu_projected WITH (NOLOCK) ON projects_records.projectedversion = projects_version_lu_projected.versionid
LEFT OUTER JOIN dbo.projects_sitetype_lu WITH (NOLOCK) ON projects_records.sitetype = projects_sitetype_lu.sitetypeid
LEFT OUTER JOIN dbo.projects_module_lu WITH (NOLOCK) ON projects_records.module = projects_module_lu.moduleid
LEFT OUTER JOIN dbo.projects_component_lu WITH (NOLOCK) ON projects_records.component = projects_component_lu.componentid
LEFT OUTER JOIN dbo.projects_browsername_lu WITH (NOLOCK) ON projects_records.browsername = projects_browsername_lu.browsernameid
LEFT OUTER JOIN dbo.projects_osname_lu WITH (NOLOCK) ON projects_records.osname = projects_osname_lu.osnameid
LEFT OUTER JOIN dbo.projects_errortype_lu WITH (NOLOCK) ON projects_records.errortype = projects_errortype_lu.errortypeid
LEFT OUTER JOIN dbo.projects_resolution WITH (NOLOCK) ON projects_records.recordid = projects_resolution.recordid
LEFT OUTER JOIN dbo.projects_sitesaffected_lu WITH (NOLOCK) ON projects_resolution.sitesaffected = projects_sitesaffected_lu.sitesaffectedid
LEFT OUTER JOIN dbo.projects_gsipriority_lu WITH (NOLOCK) ON projects_records.gsipriority = projects_gsipriority_lu.gsipriorityid
LEFT OUTER JOIN dbo.projects_clientpriority_lu WITH (NOLOCK) ON projects_records.clientpriority = projects_clientpriority_lu.clientpriorityid
LEFT OUTER JOIN dbo.projects_status_lu WITH (NOLOCK) ON projects_records.status = projects_status_lu.statusid
LEFT OUTER JOIN dbo.projects_clientaccess_lu WITH (NOLOCK) ON projects_records.clientaccess = projects_clientaccess_lu.clientaccessid
LEFT OUTER JOIN dbo.users users_assigned WITH (NOLOCK) ON projects_records.assigned = users_assigned.userid
LEFT OUTER JOIN dbo.users users_assigneddev WITH (NOLOCK) ON projects_records.assigneddev = users_assigneddev.userid
LEFT OUTER JOIN dbo.users users_assignedqa WITH (NOLOCK) ON projects_records.assignedqa = users_assignedqa.userid
LEFT OUTER JOIN dbo.users users_assignedsponsor WITH (NOLOCK) ON projects_records.assignedsponsor = users_assignedsponsor.userid
LEFT OUTER JOIN dbo.users users_createuser WITH (NOLOCK) ON projects_records.createuser = users_createuser.userid
LEFT OUTER JOIN dbo.clientusers clientusers_createuser WITH (NOLOCK) ON projects_records.createuser = clientusers_createuser.userid
LEFT OUTER JOIN dbo.projects_history_assignment WITH (NOLOCK) ON projects_history_assignment.recordid = projects_records.recordid
AND projects_history_assignment.historyid = (
SELECT ro.historyid
FROM rows rl
CROSS APPLY
(
SELECT TOP 1 rc.historyid
FROM rows rc
JOIN rows rn
ON rn.recordid = rc.recordid
AND rn.rn = rc.rn + 1
AND rn.assigned <> rc.assigned
WHERE rc.recordid = rl.recordid
ORDER BY
rc.rn
) ro
WHERE rl.rn = 1
AND rl.recordid = projects_records.recordid
)
WHERE
(#assignedtype='0' and projects_records.assigned = #assigned)
OR (#assignedtype='1' and projects_records.assigneddev = #assigned)
OR (#assignedtype='2' and projects_records.assignedqa = #assigned)
OR (#assignedtype='3' and projects_records.assignedsponsor = #assigned)
OR (#assignedtype='4' and projects_records.createuser = #assigned)
WITH rows AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ticketid ORDER BY savedate DESC) AS rn
FROM #Table
)
SELECT projects_records.recordid,
projects_records.recordtype,
/* skipped */
AND projects_history_assignment.historyid = (
SELECT ro.historyid
FROM rows rl
CROSS APPLY
(
SELECT TOP 1 rc.savedate
FROM rows rc
JOIN rows rn
ON rn.recordid = rc.recordid
AND rn.rn = rc.rn + 1
AND rn.assigned <> rc.assigned
WHERE rc.recordid = rl.recordid
ORDER BY
rc.rn
) ro
WHERE rl.rn = 1
AND rl.recordid = projects_records.recordid
)
Well, if that query does not work out -- you could always consider a trigger on the tickets_history_assignment table. You could use INSTEAD OF INSERT, UPDATE which fires before any changes are made, so you would have full control of what is about to change and what to do.