T-SQL cursor or if or case when - sql

I have this table:
Table_NAME_A:
quotid itration QStatus
--------------------------------
5329 1 Assigned
5329 2 Inreview
5329 3 sold
4329 1 sold
4329 2 sold
3214 1 assigned
3214 2 Inreview
Result output should look like this:
quotid itration QStatus
------------------------------
5329 3 sold
4329 2 sold
3214 2 Inreview
T-SQL query, so basically I want the data within "sold" status if not there then "inreview" if not there then "assigned" and also at the same time if "sold" or "inreview" or "assigned" has multiple iteration then i want the highest "iteration".
Please help me, thanks in advance :)

This is a prioritization query. One way to do this is with successive comparisons in a union all:
select a.*
from table_a a
where quote_status = 'sold'
union all
select a.*
from table_a a
where quote_status = 'Inreview' and
not exists (select 1 from table_a a2 where a2.quoteid = a.quoteid and a2.quotestatus = 'sold')
union all
select a.*
from table_a a
where quote_status = 'assigned' and
not exists (select 1
from table_a a2
where a2.quoteid = a.quoteid and a2.quotestatus in ('sold', 'Inreview')
);
For performance on a larger set of data, you would want an index on table_a(quoteid, quotestatus).

You want neither cursors nor if/then for this. Instead, you'll use a series of self-joins to get these results. I'll also use a CTE to simplify getting the max iteration at each step:
with StatusIterations As
(
SELECT quotID, MAX(itration) Iteration, QStatus
FROM table_NAME_A
GROUP BY quotID, QStats
)
select q.quotID, coalesce(sold.Iteration,rev.Iteration,asngd.Iteration) Iteration,
coalesce(sold.QStatus, rev.QStatus, asngd.QStatus) QStatus
from
--initial pass for list of quotes, to ensure every quote is included in the results
(select distinct quotID from table_NAME_A) q
--one additional pass for each possible status
left join StatusIterations sold on sold.quotID = q.quotID and sold.QStatus = 'sold'
left join StatusIterations rev on rev.quotID = q.quotID and rev.QStatus = 'Inreview'
left join StatusIterations asngd on asngd.quotID = q.quotID and asngd.QStatus = 'assigned'
If you have a table that equates a status with a numeric value, you can further improve on this:
Table: Status
QStatus Sequence
'Sold' 3
'Inreview' 2
'Assigned' 1
And the code becomes:
select t.quotID, MAX(t.itration) itration, t.QStatus
from
(
select t.quotID, MAX(s.Sequence) As Sequence
from table_NAME_A t
inner join Status s on s.QStatus = t.QStatus
group by t.quotID
) seq
inner join Status s on s.Sequence = seq.Sequence
inner join table_NAME_A t on t.quotID = seq.quotID and t.QStatus = s.QStatus
group by t.quoteID, t.QStatus
The above may look like complicated at first, but it can be faster and it will scale easily beyond three statuses without changing the code.

Related

JOIN AND CASE MORE AN TABLE

I have 2 tables; the first one ORG contains the following columns:
ORG_REF, ARB_REF, NAME, LEVEL, START_DATE
and the second one WORK contains these columns:
ARB_REF, WORK_STREET - WORK_NUM, WORK_ZIP
I want to do the following: write a select query that search in work and see if the WORK_STREET, WORK_ZIP are duplicate together, then you should look at WORK_NUM. If it is the same then output value ' ok ', but if WORK_NUM is not the same, output 'not ok'
I wrote this SQL query:
select
A.ARB_REF, A.WORK_STREET, A.WORK_NUM, A.WORK_ZIP
case when B.B = 1 then 'OK' else 'not ok' end
from
work A
join
(select
WORK_STREET, WORK_ZIP count(distinct , A.WORK_NUM) B
from
WORK
group by
WORK_STREET, WORK_ZIP) B on B.WORK_STREET = A.WORK_STREET
and B.WORK_ZIP = A.WORK_ZIP
Now I want to join the table ORG with this result I want to check if every address belong to org if it belong I should create a new column result and set it to yes in it (RESULT) AND show the "name" column otherwise set no in 'RESULT'.
Can anyone help me please?
While you can accomplish your result by adding a left outer join to the query you've already started, it might be easiest to just use count() over....
with org_data as (
-- do the inner join before the left join later
select * from org1 o1 inner join org2 o2 on o2.orgid = o1.orgid
)
select
*,
count(*) over (partition by WORK_STREET, WORKZIP) as cnt,
case when o.ARB_REF is not null then 'Yes' else 'No' end as result
from
WORK w left outer join org_data o on o.ARB_REF = w.ARB_REF

SQL getting averages with multiple joins

I'm trying to write a single query using 3 tables.
The tables and their columns that I will be using are:
Sec – ID, Symbol
Hss – Code, HDate, Holiday
Fddd – ID, Date, Price
Given a symbol AAA, I need to get the ID from the first table and match it with the ID from the third table. The second table's date must match the third table's dates with the condition of Code=1 and Holiday=1.
The Dates in the second and third table are in ascending order with most recent dates at the bottom. I want to get the average 50 day and 200 day prices. The dates in the tables are in ascending order so I want to make it descending and select the top 50 and 200 to get the average prices.
So far I can only get one average. I cannot add a second SELECT TOP 50 or add a subquery within the second avg().
SELECT AVG(TwoHun)TwoHunAvg --, AVG(Fifty) AS FiftyAvg
FROM (SELECT TOP 200 Fddd.price AS TwoHun --, TOP 50 Fddd.price AS Fifty
FROM Sec
JOIN Fddd
ON Sec.ID = Fddd.ID AND Sec.symbol = 'AAA'
JOIN Hss
ON Fddd.date = Hss.Hdate AND Hss.Code = 1 AND Hss.Holiday = 1
ORDER BY Fddd.Date DESC) AS tmp;
Thanks in advance!
Consider a union query which even scales for other Averages. I add a Type column to indicate the Averages.
SELECT '200 DAY AVG' As Type, AVG(TwoHun) As Avg
FROM
(SELECT TOP 200 Fddd.price AS TwoHun
FROM Sec
INNER JOIN Fddd ON Sec.ID = Fddd.ID
INNER JOIN Hss ON Fddd.date = Hss.Hdate
WHERE Sec.symbol = 'AAA' AND Hss.Code = 1 AND Hss.Holiday = 1
ORDER BY Fddd.Date DESC) AS tmp;
UNION
SELECT '50 DAY AVG' As Type, AVG(FiftyHun) As Avg
FROM
(SELECT TOP 50 Fddd.price AS FiftyHun
FROM Sec
INNER JOIN Fddd ON Sec.ID = Fddd.ID
INNER JOIN Hss ON Fddd.date = Hss.Hdate
WHERE Sec.symbol = 'AAA' AND Hss.Code = 1 AND Hss.Holiday = 1
ORDER BY Fddd.Date DESC) AS tmp;
Also, I moved some of your join expressions to where clause which should not change performance but does in readability.
I suspect your using SQL Server or MS Access.
One quick solution would be to have your total query as a subquery and then copy a modified version as a second subquery.
Quick rough example:
SELECT (SELECT
AVG(Fifty) AS FiftyAvg
FROM (SELECT TOP 50
Fddd.price AS Fifty
FROM Sec
JOIN Fddd
ON Sec.ID = Fddd.ID
AND Sec.symbol = 'AAA'
JOIN Hss
ON Fddd.date = Hss.Hdate
AND Hss.Code = 1
AND Hss.Holiday = 1
ORDER BY Fddd.Date DESC) AS tmp)
AS FiftyAvg,
(SELECT
AVG(TwoHun) TwoHunAvg
FROM (SELECT TOP 200
Fddd.price AS TwoHun
FROM Sec
JOIN Fddd
ON Sec.ID = Fddd.ID
AND Sec.symbol = 'AAA'
JOIN Hss
ON Fddd.date = Hss.Hdate
AND Hss.Code = 1
AND Hss.Holiday = 1
ORDER BY Fddd.Date DESC) AS tmp)
AS TwoHundredAverge;

How to use multiple count and where condition sql server 2008?

I have this two query
1.
select CL_Clients.cl_id,CL_Clients].cl_name,COUNT(*) AS number_of_orders
from CL_Clients,CLOI_ClientOrderItems
where CL_Clients.cl_id=CLOI_ClientOrderItems.cl_id
group by CL_Clients.cl_name,CL_Clients.cl_id
2.
select CL_Clients.cl_id,count(cloi_current_status) as dis
from CLOI_ClientOrderItems,CL_Clients
where cloi_current_status]='12'
and CL_Clients.cl_id=CLOI_ClientOrderItems.cl_id
group by CL_Clients.cl_name,CL_Clients.cl_id,CLOI_ClientOrderItems.cloi_current_status
i have this column i need to put count function and where condition
[cloi_current_status]
166
30
30
30
150
150
150
150
150
150
150
Quite simple, you just encapsulate the queries and give their result sets an alias and then do a JOIN between their aliases on the column that is common. (In the query below I assume you'll be joining by client id)
SELECT *
FROM (
SELECT CL_Clients.cl_id,
CL_Clients].cl_name,
COUNT(*) AS number_of_orders
FROM CL_Clients,
CLOI_ClientOrderItems
WHERE CL_Clients.cl_id = CLOI_ClientOrderItems.cl_id
GROUP BY CL_Clients.cl_name,
CL_Clients.cl_id
) A
INNER JOIN (
SELECT CL_Clients.cl_id,
count(cloi_current_status) AS dis
FROM CLOI_ClientOrderItems,
CL_Clients
WHERE cloi_current_status] = '12'
AND CL_Clients.cl_id = CLOI_ClientOrderItems.cl_id
GROUP BY CL_Clients.cl_name,
CL_Clients.cl_id,
CLOI_ClientOrderItems.cloi_current_status
) B
ON A.cl_id = B.cl_id
WHERE ...
GROUP BY ...
This will be treated as a separate result set, so you can also filter results with a WHERE or just a GROUP BY, just like in a normal SELECT.
UPDATE:
To answer the question in your comments, when you join two tables that have a column with the same value and use
SELECT * FROM A INNER JOIN B the * will show all columns returned by the join, meaning all columns from A and all columns from B, this is why you have duplicate columns.
If you want to filter the columns returned you can specifiy which columns you want returned. So, in your case, the top SELECT * can be replaced with
SELECT A.cl_id, A.cl_name, A.number_of_orders, B.dis so, your query becomes:
SELECT A.cl_id, A.cl_name, A.number_of_orders, B.dis
FROM (
SELECT CL_Clients.cl_id,
CL_Clients].cl_name,
COUNT(*) AS number_of_orders
FROM CL_Clients,
CLOI_ClientOrderItems
WHERE CL_Clients.cl_id = CLOI_ClientOrderItems.cl_id
GROUP BY CL_Clients.cl_name,
CL_Clients.cl_id
) A
INNER JOIN (
SELECT CL_Clients.cl_id,
count(cloi_current_status) AS dis
FROM CLOI_ClientOrderItems,
CL_Clients
WHERE cloi_current_status] = '12'
AND CL_Clients.cl_id = CLOI_ClientOrderItems.cl_id
GROUP BY CL_Clients.cl_name,
CL_Clients.cl_id,
CLOI_ClientOrderItems.cloi_current_status
) B
ON A.cl_id = B.cl_id
UPDATE #2:
For your last question, you need to GROUP BY at the end of the big query and use a HAVING condtion, like this:
GROUP BY A.cl_id, A.cl_name, A.number_of_orders, B.dis
HAVING COUNT(cloi_current_status) > 100
All depends on what data you are trying to get, but you can go about it like this.
SELECT Column_x, Column_y, etc..
FROM ClL_Clients a
JOIN (select CL_Clients.cl_id,CL_Clients].cl_name,COUNT(*) AS number_of_orders
from CL_Clients,CLOI_ClientOrderItems
where CL_Clients.cl_id=CLOI_ClientOrderItems.cl_id
group by CL_Clients.cl_name,CL_Clients.cl_id) b
on a.cl_id = b.cl_id
JOIN (select CL_Clients.cl_id,count(cloi_current_status) as dis
from CLOI_ClientOrderItems,CL_Clients
where cloi_current_status]='12'
and CL_Clients.cl_id=CLOI_ClientOrderItems.cl_id
group by CL_Clients.cl_name,CL_Clients.cl_id,CLOI_ClientOrderItems.cloi_current_status) c
on a.cl_id = c.cl_id
Group by BLAH BLAH
Hope this gets you in the right direction.

How to do a subquery using the result obtained in the original query?

Here is the situation I have..I have to fetch all the associated cases for a given quoteId and this requires a join of 3 tables and I am able to come up with a query for that. Below is the sample : for brevity I have omitted some table name and used only Alias name.
SELECT distinct caseTable.CASEID, quoteHdrTable.Case_UID FROM
caseTable INNER JOIN quoteHdrTable ON
quoteHdrTable.Case_UID = caseTable.Case_UID WHERE quoteHdrTable.QUOTE_ID = '12345'.
Now for each CASE_UID that returns back, I also need to display its status from a different table. That has structure below.
STATUS_TABLE
CASE_UID STATUS
------------ -----------
123 Good
234 Bad.
345 {null}
In the end I want a result like
result
case_ID case_UID status
001 123 Good
Can we use subquery to do a 2nd SQL using the result(case_UID) from first..please provide pointers or a sample SQL statement.
FYI..using DB2 database
Thanks
Sandeep
SELECT distinct c.CASEID, q.Case_UID, s.status
FROM caseTable c
INNER JOIN quoteHdrTable q ON q.Case_UID = c.Case_UID
LEFT JOIN StatusTable s ON s.CASE_UID = q.CASE_UID
WHERE quoteHdrTable.QUOTE_ID = '12345'
Why not just add another JOIN?
SELECT distinct
caseTable.CASEID,
quoteHdrTable.Case_UID,
status.STATUS
FROM caseTable
INNER JOIN quoteHdrTable
ON quoteHdrTable.Case_UID = caseTable.Case_UID
INNER JOIN STATUS_TABLE status
ON quoteHdrTable.Case_UID = status.Case_UID
WHERE quoteHdrTable.QUOTE_ID = '12345'

Using (IN operator) OR condition in Where clause as AND condition

Please look at following image, I have explained my requirements in the image.
alt text http://img30.imageshack.us/img30/5668/shippment.png
I can't use here WHERE UsageTypeid IN(1,2,3,4) because this will behave as an OR condition and fetch all records.
I just want those records, of first table, which are attached with all 4 ShipmentToID .
All others which are attached with 3 or less ShipmentToIDs are not needed in result set.
Thanks.
if (EntityId, UsageTypeId) is unique:
select s.PrimaryKeyField, s.ShipmentId from shipment s, item a
where s.PrimaryKeyField = a.EntityId and a.UsageTypeId in (1,2,3,4)
group by s.PrimaryKeyField, s.ShipmentId having count(*) = 4
otherwise, 4-way join for the 4 fields,
select distinct s.* from shipment s, item a, item b, item c, item d where
s.PrimaryKeyField = a.EntityId = b.EntityId = c.EntityId = d.EntityId and
a.UsageTypeId = 1 and b.UsageTypeId = 2 and c.UsageTypeId = 3 and
d.UsageTypeId = 4
you'll want appropriate index on (EntityId, UsageTypeId) so it doesn't hang...
If there will never be duplicates of the UsageTypeId-EntityId combo in the 2nd table, so you'll never see:
EntityUsageTypeId | EntityId | UsageTypeId
22685 | 4477 | 1
22687 | 4477 | 1
You can count matching EntityIds in that table.
WHERE (count(*) in <tablename> WHERE EntityId = 4477) = 4
DECLARE #numShippingMethods int;
SELECT #numShippingMethods = COUNT(*)
FROM shippedToTable;
SELECT tbl1.shipmentID, COUNT(UsageTypeId) as Usages
FROM tbl2 JOIN tbl1 ON tbl2.EntityId = tbl1.EntityId
GROUP BY tbl1.EntityID
HAVING COUNT(UsageTypeId) = #numShippingMethods
This way is preferred to the multiple join against same table method, as you can simply modify the IN clause and the COUNT without needing to add or subtract more tables to the query when your list of IDs changes:
select EntityId, ShipmentId
from (
select EntityId
from (
select EntityId
from EntityUsage eu
where UsageTypeId in (1,2,3,4)
group by EntityId, UsageTypeId
) b
group by EntityId
having count(*) = 4
) a
inner join Shipment s on a.EntityId = s.EntityId