Trying to optimize large sql. Have included explain output [closed] - query-optimization

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
Below is the mysql select statement followed by the explain output.
I have tried adding indexes where I thought was appropriate but with no extra speed.
There are 2 rows in the 'explain' output that stand out (id 7 and 8 where the rows are over 35,000).
I think the sql needs to be restructured but I don't know how to change it. Any help would be appreciated.
EXPLAIN SELECT wo.JobStatusID
,wo.JobNo
,ql.RackCode
,ql.Description
,c.CustName
,s.ShipDate
,s.Qty
,pl.PrepLocation
,wl.WeldLocation
,line.WeldLine
,pm.Initials
,pqty.ShipTotal
,pqty.POTotal
,po.POCount
,s2.Notes
,bm.UnitQty
,labour.AssemblyLabour
,labour.WeldLabour
,labour.TotalGUTLabour + labour.TotalGUTSetUp / pqty.POTotal AS GUTLabour
,labour.TotalPrepLabour + labour.TotalPrepSetUp / pqty.POTotal AS PrepLabour
,pqty.POTotal
FROM WorkOrders wo
JOIN (
SELECT JobNo
,MasterJobNo AS RelevantJobNo
FROM WorkOrders
WHERE MasterJobNo != 0
UNION
SELECT JobNo
,JobNo AS RelevantJobNo
FROM WorkOrders
WHERE MasterJobNo = 0
) AS r
ON r.JobNo = wo.JobNo
LEFT JOIN QuoteLog ql
ON ql.QuoteID = wo.QuoteID
AND wo.FOCPlayArea = 0
LEFT JOIN Customer c
ON c.CustID = ql.CustID
LEFT JOIN WOSchedule s
ON s.JobNo = wo.JobNo
AND s.ObsoleteInd = 0
LEFT JOIN (
SELECT JobNo
,Notes
FROM WOSchedule
ORDER BY ShipDate DESC
) AS s2
ON s2.JobNo = wo.JobNo
LEFT JOIN PrepLocation pl
ON pl.PrepLocationID = wo.PrepLocationID
LEFT JOIN WeldLocation wl
ON wl.WeldLocationID = wo.WeldLocationID
LEFT JOIN ProgramManager pm
ON pm.ProgramManagerID = wo.ProgramManagerID
LEFT JOIN (
SELECT JobNo
,SUM(POQty) AS POTotal
,SUM(ShipQty) AS ShipTotal
FROM WOOrderDetails wod
JOIN WODetailType wodt
ON wodt.DetailTypeID = wod.DetailTypeID
AND wodt.ReleaseModeID = 1
AND wodt.Inactive = 0
WHERE wod.ObsoleteInd = 0
GROUP BY wod.JobNo
) AS pqty
ON pqty.JobNo = wo.JobNo
LEFT JOIN (
SELECT Jobno
,COUNT(*) AS POCount
FROM WOPOs
GROUP BY JobNo
) AS po
ON po.JobNo = wo.JobNo
LEFT JOIN BOMMaterialList bm
ON bm.JobNo = r.RelevantJobNo
LEFT JOIN WeldLine line
ON wo.WeldLineID = line.WeldLineID
LEFT JOIN (
SELECT x.JobNo
,x.BOMDetailID
,SUM(x.TotalPrepLabour) AS TotalPrepLabour
,SUM(x.TotalPrepSetUp) AS TotalPrepSetUp
,SUM(x.TotalGUTLabour) AS TotalGUTLabour
,SUM(x.TotalGUTSetUp) AS TotalGUTSetUp
,SUM(x.WeldLabour * x.WeldEfficiency) AS WeldLabour
,SUM(AssemblyLabour * AssemblyEfficiency * UnitQty) AS AssemblyLabour
FROM (
SELECT bm.JobNo
,bm.BOMDetailID
,bm.UnitQty
,SUM(bm.UnitQty * pl.HitTime * pl.NoHits * pl.NoMen) AS TotalPrepLabour
,SUM(pl.SetUp) AS TotalPrepSetUp
,SUM(bm.UnitQty * gl.HitTime * gl.NoHits * gl.NoMen) AS TotalGUTLabour
,SUM(gl.SetUp) AS TotalGUTSetUp
,bm.WeldLabour
,bm.WeldEfficiency
,bm.AssemblyLabour
,bm.AssemblyEfficiency
FROM BOMMaterialList bm
LEFT JOIN BOMPrepLabour pl
ON pl.BOMDetailID = bm.BOMDetailID
LEFT JOIN BOMGUTLabour gl
ON gl.BOMDetailID = bm.BOMDetailID
GROUP BY BOMDetailID
) AS x
GROUP BY x.JobNo
) AS labour
ON bm.BOMDetailID = labour.BOMDetailID
WHERE ( wo.JobStatusID = 1
OR wo.JobStatusID = 2
OR wo.JobStatusID = 3
)
GROUP BY wo.JobNo
ORDER BY wo.JobStatusID
,wl.WeldLocation
,line.WeldLine
,wo.JobNo;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 1418 "Using temporary; Using filesort"
1 PRIMARY wo eq_ref PRIMARY,IDX_1 PRIMARY 4 r.JobNo 1 "Using where"
1 PRIMARY ql eq_ref PRIMARY PRIMARY 4 sbpdf3_custom014.wo.QuoteID 1
1 PRIMARY c eq_ref PRIMARY PRIMARY 2 sbpdf3_custom014.ql.CustID 1
1 PRIMARY s ref PRIMARY,IDX_2 PRIMARY 4 r.JobNo 28
1 PRIMARY <derived4> ALL NULL NULL NULL NULL 2884
1 PRIMARY pl eq_ref PRIMARY PRIMARY 4 sbpdf3_custom014.wo.PrepLocationID 1
1 PRIMARY wl eq_ref PRIMARY PRIMARY 4 sbpdf3_custom014.wo.WeldLocationID 1
1 PRIMARY pm eq_ref PRIMARY PRIMARY 4 sbpdf3_custom014.wo.ProgramManagerID 1
1 PRIMARY <derived5> ALL NULL NULL NULL NULL 1244
1 PRIMARY <derived6> ALL NULL NULL NULL NULL 1310
1 PRIMARY bm ref IDX1 IDX1 4 r.RelevantJobNo 19
1 PRIMARY line ref PRIMARY PRIMARY 4 sbpdf3_custom014.wo.WeldLineID 1
1 PRIMARY <derived7> ALL NULL NULL NULL NULL 1102
7 DERIVED <derived8> ALL NULL NULL NULL NULL 35736 "Using temporary; Using filesort"
8 DERIVED bm index NULL PRIMARY 4 NULL 35268
8 DERIVED pl ref PRIMARY PRIMARY 4 sbpdf3_custom014.bm.BOMDetailID 1
8 DERIVED gl ref PRIMARY PRIMARY 4 sbpdf3_custom014.bm.BOMDetailID 1
6 DERIVED WOPOs index NULL IDX_1 4 NULL 1804 "Using index"
5 DERIVED wod ref IDX_1 IDX_1 1 1660 "Using where; Using temporary; Using filesort"
5 DERIVED wodt eq_ref PRIMARY PRIMARY 2 sbpdf3_custom014.wod.DetailTypeID 1 "Using where"
4 DERIVED WOSchedule ALL NULL NULL NULL NULL 2884 "Using filesort"
2 DERIVED WorkOrders range IDX_2 IDX_2 4 NULL 15 "Using where; Using index"
3 UNION WorkOrders ref IDX_2 IDX_2 4 689 "Using index"
NULL "UNION RESULT" <union2,3> ALL NULL NULL NULL NULL NULL

There's a lot to look through here, but I did spot this subquery:
SELECT JobNo
,MasterJobNo AS RelevantJobNo
FROM WorkOrders
WHERE MasterJobNo != 0
UNION
SELECT JobNo
,JobNo AS RelevantJobNo
FROM WorkOrders
WHERE MasterJobNo = 0
which I believe could be re-written like so:
SELECT JobNo
,CASE WHEN MasterJobNo != 0
THEN MasterJobNo
ELSE JobNo END AS RelevantJobNo
FROM WorkOrders
And once we re-write it this way, we wonder why it needs to be a subquery, rather than simply joining to the table. And then I look again and see that we've already used this table in the query, and so we can eliminate that join entirely, and just include the CASE expression in the appropriate places. Moreover, this expression is only used in one place that I could spot (the join to BOMMaterialList).
I also noticed this subquery:
SELECT JobNo
,Notes
FROM WOSchedule
ORDER BY ShipDate DESC
I think we can eliminate this, as well. ORDER BY without a LIMIT has no meaning here when used as a derived table, and again: this table is already included in the query in another place. The only difference between the data provided this s2 subquery and the prior s table is the check on the ObsoleteInd field, and the only place data from s2 is used elsewhere is the Notes field in the select clause. The s table is only used in select list of the ShipDate and Qty items.
So I can get us down to this, which should show some improvement:
SELECT wo.JobStatusID
,wo.JobNo
,ql.RackCode
,ql.Description
,c.CustName
,case when s.ObsoleteInd = 0 then s.ShipDate else null end as ShipDate
,case when s.ObsoleteInd = 0 then s.Qty else null end as Qty
,pl.PrepLocation
,wl.WeldLocation
,line.WeldLine
,pm.Initials
,pqty.ShipTotal
,pqty.POTotal
,po.POCount
,s.Notes
,bm.UnitQty
,labour.AssemblyLabour
,labour.WeldLabour
,labour.TotalGUTLabour + labour.TotalGUTSetUp / pqty.POTotal AS GUTLabour
,labour.TotalPrepLabour + labour.TotalPrepSetUp / pqty.POTotal AS PrepLabour
,pqty.POTotal
FROM WorkOrders wo
LEFT JOIN QuoteLog ql
ON ql.QuoteID = wo.QuoteID
AND wo.FOCPlayArea = 0
LEFT JOIN Customer c
ON c.CustID = ql.CustID
LEFT JOIN WOSchedule s
ON s.JobNo = wo.JobNo
LEFT JOIN PrepLocation pl
ON pl.PrepLocationID = wo.PrepLocationID
LEFT JOIN WeldLocation wl
ON wl.WeldLocationID = wo.WeldLocationID
LEFT JOIN ProgramManager pm
ON pm.ProgramManagerID = wo.ProgramManagerID
LEFT JOIN (
SELECT JobNo
,SUM(POQty) AS POTotal
,SUM(ShipQty) AS ShipTotal
FROM WOOrderDetails wod
JOIN WODetailType wodt
ON wodt.DetailTypeID = wod.DetailTypeID
AND wodt.ReleaseModeID = 1
AND wodt.Inactive = 0
WHERE wod.ObsoleteInd = 0
GROUP BY wod.JobNo
) AS pqty
ON pqty.JobNo = wo.JobNo
LEFT JOIN (
SELECT Jobno
,COUNT(*) AS POCount
FROM WOPOs
GROUP BY JobNo
) AS po
ON po.JobNo = wo.JobNo
LEFT JOIN BOMMaterialList bm
ON bm.JobNo = CASE WHEN wo.MasterJobNo != 0 THEN wo.MasterJobNo ELSE wo.JobNo END
LEFT JOIN WeldLine line
ON wo.WeldLineID = line.WeldLineID
LEFT JOIN (
SELECT x.JobNo
,x.BOMDetailID
,SUM(x.TotalPrepLabour) AS TotalPrepLabour
,SUM(x.TotalPrepSetUp) AS TotalPrepSetUp
,SUM(x.TotalGUTLabour) AS TotalGUTLabour
,SUM(x.TotalGUTSetUp) AS TotalGUTSetUp
,SUM(x.WeldLabour * x.WeldEfficiency) AS WeldLabour
,SUM(AssemblyLabour * AssemblyEfficiency * UnitQty) AS AssemblyLabour
FROM (
SELECT bm.JobNo
,bm.BOMDetailID
,bm.UnitQty
,SUM(bm.UnitQty * pl.HitTime * pl.NoHits * pl.NoMen) AS TotalPrepLabour
,SUM(pl.SetUp) AS TotalPrepSetUp
,SUM(bm.UnitQty * gl.HitTime * gl.NoHits * gl.NoMen) AS TotalGUTLabour
,SUM(gl.SetUp) AS TotalGUTSetUp
,bm.WeldLabour
,bm.WeldEfficiency
,bm.AssemblyLabour
,bm.AssemblyEfficiency
FROM BOMMaterialList bm
LEFT JOIN BOMPrepLabour pl
ON pl.BOMDetailID = bm.BOMDetailID
LEFT JOIN BOMGUTLabour gl
ON gl.BOMDetailID = bm.BOMDetailID
GROUP BY BOMDetailID
) AS x
GROUP BY x.JobNo
) AS labour
ON bm.BOMDetailID = labour.BOMDetailID
WHERE ( wo.JobStatusID = 1
OR wo.JobStatusID = 2
OR wo.JobStatusID = 3
)
GROUP BY wo.JobNo
ORDER BY wo.JobStatusID
,wl.WeldLocation
,line.WeldLine
,wo.JobNo;
There are likely a few other improvements like this possible, but I suspect most improvement will come from examining your index use.

Related

T-SQL query to show all the past steps, active and future steps

I have 3 tables in SQL Server:
map_table: (workflow map path)
stepId step_name
----------------
1 A
2 B
3 C
4 D
5 E
history_table:
stepId timestamp author
----------------------------
1 9:00am John
2 9:20am Mary
current_stageTable:
Id currentStageId waitingFor
------------------------------------
12345 3 Kat
I would like to write a query to show the map with the workflow status. Like this result here:
step name time author
----------------------------
1 A 9:00am John
2 B 9:20am Mary
3 C waiting Kat
4 D
5 E
I tried left join
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
I thought it will list all the records from the map table, since I am using left join, but somehow it only shows 3 records which is from history table..
So I changed to
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
union
select
m.stepId, m.step_name, '' as timestamp, '' as author
from
map_table m
where
m.stageId not in (select stageId from history_table)
order by
m.stepId
Then it list the result almost as I expected, but how do I add the 3rd table in to show the current active stage?
Thank you very much for all your help!! Much appreciated.
Looks like it's what you asked:
with map_table as (
select * from (values (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
,(5,'E')) t(stepId, step_name)
)
, history_table as (
select * from (values
(1,'9:00am','John')
,(2,'9:20am','Mary')) t(stepId, timestamp, author)
)
, current_stapeTable as (
select * from (values (2345, 3, 'Kat')) t(Id, currentStageId, waitingFor)
)
select
m.stepId, m.step_name
, time = coalesce(h.timestamp, case when c.waitingFor is not null then 'waiting' end)
, author = coalesce(h.author, c.waitingFor)
from
map_table m
left join history_table h on m.stepId = h.stepId
left join current_stapeTable c on m.stepId = c.currentStageId
I think a union fits well with the data and avoids the coalescing the values on multiple joins.
with timeline as (
select stepId, "timestamp" as ts, author from history_table
union all
select currentStageId, 'waiting', waitingFor from current_stageTable
)
select step_id, step_name, "timestamp", author
from
map_table as m left outer join timeline as t
on t.stepId = m.stepId

Select Count Top Inner Join and Where Clause in SQL

This is my Query:
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
It returns this table:
ExamID StudentID ItemNo StudentAnswer Score
1006 1 1 A 25
1005 1 2 B 30
1004 1 3 A 35
What i want to do is it will return 2 if StudentAnswer='A' and 1 if StudentAnswer='B'
Guys there is nothing wrong with my query on top. What i am asking is what should I add in that query.
I have this which in my mind should return 2 but its an error.
Select COUNT(*) From (
Select Top 3 TableStudentAnswer.ExamID, TableStudentAnswer.StudentID, TableStudentAnswer.ItemNo, TableStudentAnswer.StudentAnswer, TableScore.Score
from TableStudentAnswer
Inner join TableScore on TableStudentAnswer.StudentID=TableScore.StudentID and TableStudentAnswer.ExamID=TableScore.ExamID
where TableStudentAnswer.ExamID=1 and TableStudentAnswer.ItemNo=1
Order By TableScore.Score Asc) where TableStudentAnswer.StudentAnswer = 'A'
It should return:
2
Please help me!
Will this do?
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score,
case
when tablestudentanswer.studentanswer = 'A' then 2
when tablestudentanswer.studentanswer = 'B' then 1
else NULL
end as [MyColumn]
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
Your question is a bit unclear. Perhaps you want the amount of answers for each?
count(1) over (partition by tablestudentanswer.studentanswer)
This will give you a column with the amount of all the answers with the given studentanswer to each of the rows in the result set. However, note that this could be quite slow. If you can, you're better off using a normal group by.
Do you mean you would like the query to return the number of answers? If so, using COUNT may help.
SELECT tablestudentanswer.studentid,
tablestudentanswer.studentanswer
COUNT(1) AS NumberOfAnswers
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
GROUP BY tablestudentanswer.studentid, tablestudentanswer.studentanswer
Please correct me if I am wrong.
By the way, why does your result table doesn't consist of itemno even though you have it in your SELECT statement?

How to combine fields from 2 columns to create a "matrix"?

I have a logging table in my application that only logs changed data, and leaves the other columns NULL. What I'm wanting to do now is create a view that takes 2 of those columns (Type and Status),
and create a resultset that returns the Type and Status on the entry of that log row, assuming that either one or both columns could be null.
For example, with this data:
Type Status AddDt
A 1 7/8/2013
NULL 2 7/7/2013
NULL 3 7/6/2013
NULL NULL 7/5/2013
B NULL 7/4/2013
C NULL 7/3/2013
C 4 7/2/2013
produce the resultset:
Type Status AddDt
A 1 7/8/2013
A 2 7/7/2013
A 3 7/6/2013
A 3 7/5/2013
B 3 7/4/2013
C 3 7/3/2013
C 4 7/2/2013
From there I'm going to figure out the first time in these results the Type and Status meet certain conditions, such as a Type of B and Status 3 (7/4/2013) and ultimately use that date in a calculation, so performance is a huge issue with this.
Here's what I was thinking so far, but it doesn't get me where I need to be:
SELECT
Type.TypeDesc
, Status.StatusDesc
, *
FROM
jw50_Item c
OUTER APPLY (SELECT TOP 10000 * FROM jw50_ItemLog csh WHERE csh.ItemID = c.ItemID AND csh.StatusCode = 'OPN' ORDER BY csh.AddDt DESC) [Status]
OUTER APPLY (SELECT TOP 10000 * FROM jw50_ItemLog cth WHERE cth.ItemID = c.ItemID AND cth.ItemTypeCode IN ('F','FG','NG','PF','SXA','AB') ORDER BY cth.AddDt DESC) Type
WHERE
c.ItemID = #ItemID
So with the help provided below, I was able to get where I needed. Here is my final solution:
SELECT
OrderID
, CustomerNum
, OrderTitle
, ItemTypeDesc
, ItemTypeCode
, StatusCode
, OrdertatusDesc
FROM
jw50_Order c1
OUTER APPLY (SELECT TOP 1 [DateTime] FROM
(SELECT c.ItemTypeCode, c.OrderStatusCode, c.OrderStatusDt as [DateTime] FROM jw50_Order c WHERE c.OrderID = c1.OrderID
UNION
select (select top 1 c2.ItemTypeCode
from jw50_OrderLog c2
where c2.UpdatedDt >= c.UpdatedDt and c2.ItemTypeCode is not null and c2.OrderID = c.OrderID
order by UpdatedDt DESC
) as type,
(select top 1 c2.StatusCode
from jw50_OrderLog c2
where c2.UpdatedDt >= c.UpdatedDt and c2.StatusCode is not null and c2.OrderID = c.OrderID
order by UpdatedDt DESC
) as status,
UpdatedDt as [DateTime]
from jw50_OrderLog c
where c.OrderID = c1.OrderID AND (c.StatusCode IS NOT NULL OR c.ItemTypeCode IS NOT NULL)
) t
WHERE t.ItemTypeCode IN ('F','FG','NG','PF','SXA','AB') AND t.StatusCode IN ('OPEN')
order by [DateTime]) quart
WHERE quart.DateTime <= #FiscalPeriod2 AND c1.StatusCode = 'OPEN'
Order By c1.OrderID
The union is to bring in the current data in addition to the log table data to create the resultset, since the current data maybe what meets the conditions required. Thanks again for the help guys.
Here is an approach that uses correlated subqueries:
select (select top 1 c2.type
from jw50_Item c2
where c2.AddDt >= c.AddDt and c2.type is not null
order by AddDt
) as type,
(select top 1 c2.status
from jw50_Item c2
where c2.AddDt >= c.AddDt and c2.status is not null
order by AddDt
) as status,
(select AddDt
from jw50_Item c
If you have indexes on jw50_item(AddDt, type) and jw50_item(AddDt, status), then the performance should be pretty good.
I suppose you want to "generate a history": for those dates that has some data missing, the next available data should be set.
Something similar should work:
Select i.AddDt, t.Type, s.Status
from Items i
join Items t on (t.addDt =
(select min(t1.addDt)
from Items t1
where t1.addDt >= i.addDt
and t1.Type is not null))
join Items s on (s.addDt =
(select min(s1.addDt)
from Items s1
where s1.addDt >= i.addDt
and s1.status is not null))
Actually I'm joining the base table to 2 secondary tables and the join condition is that we match the smallest row where the respective column in the secondary table is not null (and of course smaller than the current date).
I'm not absolutely sure that it will work, since I don't have an SQL Server in front of me but give it a try :)

Joining two queries horizontally

I wrote two queries below that produce one row of data each.
What is the best way to combine them such that I am LEFT with only a single row of data?
These are coming FROM two DISTINCT databases named : [ASN01] and [dsi_ASN_dsicx]
I have 70 pairs of databases like this but am showing only one for simplicity.
The fact that the three letter acronym ASN is common to both database names is no mistake and if needed can be a part of the solution.
Current Results:
Site, Elligence (header)
ASN, 100.00
Site, GP_Total (header)
ASN, 120.00
Desired results:
Site, GP_Total, Elligence (header)
ASN, 120.00, 100.00
SELECT 'ASN' AS Site ,
CASE SUM(perdblnc)
WHEN NULL THEN 0
ELSE -1 * SUM(PERDBLNC)
END AS GP_Total
FROM [ASN01].[dbo].[GL10110] T1
LEFT OUTER JOIN [ASN01].[dbo].[GL00105] T2 ON [T1].[ACTINDX] = [T2].[ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID IN ( '2' )
AND ACTNUMST IN ( '4200-0000-C', '6940-0000-C', '6945-0000-C',
'6950-0000-C' )
SELECT 'ASN' AS [Site] ,
SUM(pi.amount) AS [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi ON c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p ON pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt ON p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod NOT IN ( '5', '7' )
AND pt.paymentmethod IS NOT NULL
AND p.sdate >= '20120201'
AND p.sdate <= '20120229'
WIthout going through and changing any of your queries, the easiest way would be to use temp tables using the "WITH" common_table_expression. Table1 and Table2 are temp tables created from your select statements. Therefore, we select table1 and join table2.
Let me know if there are any syntax problems, I don't have anything to test this on presently.
;With Table1 as (SELECT 'ASN' as Site, Case sum(perdblnc)
WHEN NULL THEN 0
ELSE -1*sum(PERDBLNC) END as GP_Total
FROM [ASN01].[dbo].[GL10110] T1
Left Outer Join [ASN01].[dbo].[GL00105] T2
ON [T1]. [ACTINDX]= [T2]. [ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID in ('2')
AND ACTNUMST in ('4200-0000-C', '6940-0000-C', '6945-0000-C', '6950-0000-C'))
, Table2 as (SELECT
'ASN' as [Site],
SUM(pi.amount) as [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi on c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p on pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt on p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod not in ('5','7')
AND pt.paymentmethod is not null
AND p.sdate >='20120201' and p.sdate <= '20120229')
SELECT * FROM Table1
LEFT JOIN Table2 ON Table1.site = Table2.site
Hope this helps! Marks as answer if it is =)

SQL query - need to exclude if Requirement NOT met, and exclude if Disqualifier IS met

I have a feeling once i see the solution i'll slap my forehead, but right now I'm not seeing it.
I have a lookup table, say TableB, which looks like this. All fields are INT except the last two which are BOOL.
ID, TableA_ID, Value, Required, Disqualifies
I have a list of TableA_Id values (1, 2, 3 ) etc
For each record in this table, either Required can be true or disqualified can be true - they cant both be true at the same time. They can both be false or null though. There can be duplicate values of TableA_Id but there should never be duplicates of TableA_Id and Value
If required is true for any of those TableA_ID values, and none of those values are in my list, return no records. If none of the values are marked as required (required = 0 or null) then return records UNLESS any of the values are marked as Disqualifies and are in the list, in which case i want to return no records.
So - if a field is required and i dont have it, dont return any records. If a field is marked as disqualified and i have it, don't return any records. Only return a record if either i have a required value or don't have a disqualified value or there are no required values.
I hope I explained myself clearly.
Thanks in advance for pointing me in the right direction.
As an example of what my records might look like:
ID TableA_ID Value Required Disqualifies
-- --------- ----- -------- ------------
1 123 1 True False
2 123 2 True False
3 123 3 False False
4 123 4 False True
5 456 1 False True
6 456 2 False False
Given this set of sample data, if we're working with TableA_Id 123 and my list of values is lets say 1 and 3, i would get data returned because i have a required value and dont have any disqualified values. If my list of values were just 3, i'd get no records since i'm missing of the Required values. If my list of values were 1 and 4, i'd get no records because 4 is marked as disqualified.
Now if we're working with TableA_Id 456, the only list of values that would return any records is 2.
Maybe i should post the whole SQL query - i was trying to keep this short to make it easier for everyone, but it looks like maybe that's not working so well.
Here is the full dynamically generated query. The bit i am working on now is the 2nd line from the bottom. To equate this to my example, t.id would be TableA_ID, Value would be PDT_ID.
SELECT DISTINCT t.ID, t.BriefTitle, stat.Status, lstat.Status AS LocationStatus, st.SType, t.LAgency, l.City, state.StateCode
,( SELECT TOP 1 UserID
FROM TRecruiter
WHERE TrialID = t.ID AND Lead = 1 ), l.ID as LocationID
, l.WebBased
FROM Trial t
INNER JOIN Location l ON t.ID = l.TrialID
FULL JOIN pdt on t.ID = pdt.trialid
FULL JOIN pdm on t.ID = pdm.TrialID
FULL JOIN s on t.ID = s.TrialID
FULL JOIN hy on t.ID = hy.TrialID
FULL JOIN ta on t.ID = ta.TrialID
FULL JOIN stt on t.ID = stt.TrialID
FULL JOIN [Status] stat ON t.StatusID = stat.ID
FULL JOIN st ON t.StudyTypeID = st.ID
FULL JOIN State state ON l.StateID = state.ID
FULL JOIN [Status] lstat ON l.StatusID = lstat.ID
FULL JOIN ts ON t.ID = ts.TrialID
FULL JOIN tpdm ON t.ID = tpdm.TrialID
WHERE ((t.ID IS NOT NULL)
AND (EligibleHealthyVolunteers IS NULL OR EligibleHealthyVolunteers = 1 OR (0 = 0 AND EligibleHealthyVolunteers = 0))
AND (eligiblegenderid is null OR eligiblegenderid = 1 OR eligiblegenderid = 3)
AND ((EligibleMinAge <= 28 AND EligibleMaxAge >= 28) OR (EligibleMinAge <= 28 AND EligibleMaxAge is null) OR (EligibleMinAge IS NULL AND EligibleMaxAge >= 28))
AND (HYID = 6 AND (hy.Disqualify = 0 OR hy.Disqualify IS NULL AND NOT EXISTS (SELECT * FROM hy WHERE t.id = hy.TrialID AND hy.Req =1)) OR HYID = 6 AND hy.req = 1)
AND (PDT_ID IN (1) AND ( pdt.Disqualify = 0 OR pdt.Disqualify IS NULL AND NOT EXISTS (select * from pdt where t.id = pdt.TrialID AND pdt.Req = 1)) OR PDT_ID IN (1) AND (pdt.Req = 1 AND (pdt.Disqualify = 0 or pdt.Disqualify is null )))
) AND ((3959 * acos(cos(radians(34.18)) * cos(radians(l.Latitude)) * cos(radians(l.Longitude) - radians(-118.46)) + sin(radians(34.18)) * sin(radians(l.Latitude)))) <= 300 OR l.Latitude IS NULL) AND t.IsPublished = 1 AND (t.StatusID = 1 OR t.StatusID = 2)
I've changed/shortened some table names just for security/privacy reasons.
Edit:
I think i am close to getting this working, but I'm getting tripped up on the logic again.
I have the following bit of sql:
AND ( exists (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id AND pdT_ID IN (2) ) AND EXISTS (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id ) )
I'm not sure how to structure this. Those two exists statement should make the whole thing true in the following combination:
True & False
True & True
False & False
If it's False & True, then the whole thing is false. In other words if there is a Req =1 AND the PDT_ID that is marked as Req=1 is not in our list (in the example above the list just contains '2') then return false.
EDIT:
I think i finally got it.
AND NOT EXISTS (SELECT * FROM pdt WHERE Disqualify = 1 AND trialid = t.id AND PDT_ID IN (2) )
AND NOT ( NOT exists (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id AND PDT_ID IN (2) ) AND EXISTS (SELECT * FROM pdt WHERE Req = 1 AND trialid = t.id ) )
So far this seems to work in testing. Although I'm only working with two values of PDT_ID. If this does resolve my problem, i will come back and give someone the credit for helping me.
SELECT *
FROM TABLEB B
WHERE
(
B.REQUIRED = 1
AND EXISTS
(
SELECT 1
FROM TABLEA A
WHERE A.ID =B.TABLEA_ID
)
)
OR
(
B.REQUIRED != 1
AND B.DISQUALIFIES <> 1
)
OR
(
B.REQUIRED != 1
AND B.DISQUALIFIES = 1
AND EXISTS
(
SELECT 1
FROM TABLEA A
WHERE A.ID =B.TABLEA_ID
)
)
UPDATE - after the EDIT and explanation from OP:
Change the line
FULL JOIN pdt on t.ID = pdt.trialid
To
FULL JOIN (SELECT * FROM pdt BB WHERE
BB.TrialID IN (SELECT AA.ID FROM Trial AA WHERE AA.ID = BB.TrialID) AND
1 > (SELECT COUNT(*) FROM Trial A
LEFT OUTER JOIN pdt B ON B.Req != 1 AND B.Disqualify != 1 AND B.TrialID = A.ID
WHERE B.TrialID IS NULL)) pdt ON t.ID = pdt.TiralID
AND change the line before last from
AND (PDT_ID IN (1) AND ( pdt.Disqualify = 0 OR pdt.Disqualify IS NULL AND NOT EXISTS (select * from pdt where t.id = pdt.TrialID AND pdt.Req = 1)) OR PDT_ID IN (1) AND (pdt.Req = 1 AND (pdt.Disqualify = 0 or pdt.Disqualify is null )))
To
AND PDT_ID IN (1)
(You seem to have found a solution, yet I've decided to share my thoughts about this problem anyway.)
Given you've got a set of TableA IDs, each of which is accompanied by a set of some values, and you want to test the entire row set against this TableB thing using the rules you've set forth, I think the entire checking process might look like this:
Match every pair of TableA.ID and Value against TableB and get aggregate maximums of Required and Disqualifies for every TableA.ID along the way.
Derive a separate list of TableA_ID values with their corresponding maximum values of Required, from TableB. That will be for us to know whether a particular TableA_ID must have a required value at all.
Match the row set obtained at Stage 1 against the derived table (Stage 2) and check the aggregate values:
1) if the actual aggregate Disqualifies for a TableA_ID is 1, discard this TableA_ID set;
2) if a TableA_ID has a match in the Stage 2 derived table and the aggregate maximum of Required that we obtained at Stage 1 doesn't match the maximum Required in the derived table, discard the set as well.
Something tells me that it would be better at this point to move on to some sort of illustration. Here's a sample script, with comments explaining which part of the script implements which part of the description above:
;
WITH
/* this is the row set to be tested and which
is supposed to contain TableA.IDs and Values */
testedRowSet AS (
SELECT
TableA.ID AS TableA_ID,
SomethingElse.TestedValue AS Value,
...
FROM TableA
JOIN SomethingElse ON some_condition
...
),
/* at this point, we are getting the aggregate maximums
of TableB.Required and TableB.Disqualifies for every
TableA_ID in testedRowSet */
aggregated AS (
SELECT
testedRowSet.TableA_ID,
testedRowSet.Value,
...
DoesHaveRequiredValues = MAX(CASE TableB.Required WHEN 1 THEN 1 ELSE 0 END) OVER (PARTITION BY testedRowSet.TableA_ID),
HasDisqualifyingValues = MAX(CASE TableB.Disqualifies WHEN 1 THEN 1 ELSE 0 END) OVER (PARTITION BY testedRowSet.TableA_ID)
FROM testedRowSet
LEFT JOIN TableB ON testedRowSet.TableA_ID = TableB.TableA_ID
AND testedRowSet.Value = TableB.Value
),
/* this row set will let us see whether a particular
TableA_ID must have a required value */
properties AS (
SELECT
TableA_ID,
MustHaveRequiredValues = MAX(CASE Required WHEN 1 THEN 1 ELSE 0 END)
FROM TableB
GROUP BY TableA_ID
),
/* this is where we are actually checking the previously
obtained aggregate values of Required and Disqualifies */
tested AS (
SELECT
aggregated.TableA_ID,
aggregated.Value,
...
FROM aggregated
LEFT JOIN properties ON aggregated.TableA_ID = properties.TableA_ID
WHERE aggregated.HasDisqualifyingValues = 0
AND (properties.TableA_ID IS NULL
OR properties.MustHaveRequiredValues = aggregated.DoesHaveRequiredValues)
)
SELECT * FROM tested

Resources