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

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

Related

How to find difference between table with multiple conditions

I have exact two tables but some value differences. So I would like to find those differences with condition that if the column value has a difference of more than 10.
For example, all 9 columns have the same values in both tables, but the difference between the values column is 11, so this record is different. If the value difference is 9 so records are the same.
So I wrote this query to get differences:
select *
from test.test m
inner join test.test1 t
on
m.month_date = t.month_date and
m.level_1 = t.level_1 and
m.level_2 = t.level_2 and
m.level_3 = t.level_3 and
m.level_4 = t.level_4 and
m.level_header = t.level_header and
m.unit = t.unit and
m.model_type_id = t.model_type_id and
m.model_version_desc = t.model_version_desc
where m.month_date = '2022-11-01' and abs(m.value - t.value) > 10)
so this returns me all records that all column values are matched but did not pass the value difference condition.
Second, i have full outer join to get all differences
select *
from test.test m
full outer join test.test1 t
on
m.month_date = t.month_date and
m.level_1 = t.level_1 and
m.level_2 = t.level_2 and
m.level_3 = t.level_3 and
m.level_4 = t.level_4 and
m.level_header = t.level_header and
m.unit = t.unit and
m.model_type_id = t.model_type_id and
m.model_version_desc = t.model_version_desc
where m.month_date is null or t.month_date is null and m.month_date = '2022-11-01'
How can I combine the results of these two queries without UNION? I want to have only one query (sub query is acceptable)
Assuming that for a given day, you need to find
rows that match between the tables but exceed the value difference threshold
AND
rows present in either left or right table, that don't have a corresponding row in the other table
select *
from test.test m
full outer join test.test1 t
using (
month_date,
level_1,
level_2,
level_3,
level_4,
level_header,
unit,
model_type_id,
model_version_desc )
where (m.month_date is null
or t.month_date is null
and m.month_date = '2022-11-01' )
or (m.month_date = '2022-11-01' and abs(m.value - t.value) > 10);
Online demo
Since the columns used to join the tables have the same names, you can shorten their list by swapping out the lengthy table1.column1=table2.column1 and... list of pairs for a single USING (month_date,level_1,level_2,level_3,...) (doc). As a bonus, it will avoid listing the matching columns twice in your output, once for the left table, once for the right table.
select *
from (select 1,2,3) as t1(a,b,c)
full outer join
(select 1,2,3) as t2(a,b,c)
on t1.a=t2.a
and t1.b=t2.b
and t1.c=t2.c;
-- a | b | c | a | b | c
-----+---+---+---+---+---
-- 1 | 2 | 3 | 1 | 2 | 3
select *
from (select 1,2,3) as t1(a,b,c)
full outer join
(select 1,2,3) as t2(a,b,c)
using(a,b,c);
-- a | b | c
-----+---+---
-- 1 | 2 | 3
In your first query, you can replace the null values for a specific number. Something like this:
where m.month_date = '2022-11-01' and abs(ISNULL(m.value,-99) - ISNULL(t.value,-99)) > 10)
The above will replace the nulls for -99 (choose an appropriate value for your data), so if you have that m.value is 10 and t.value is null, then should be returned in your first query.

T-SQL cursor or if or case when

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.

Is there a simpler way to write this query? [MS SQL Server]

I'm wondering if there is a simpler way to accomplish my goal than what I've come up with.
I am returning a specific attribute that applies to an object. The objects go through multiple iterations and the attributes might change slightly from iteration to iteration. The iteration will only be added to the table if the attribute changes. So the most recent iteration might not be in the table.
Each attribute is uniquely identified by a combination of the Attribute ID (AttribId) and Generation ID (GenId).
Object_Table
ObjectId | AttribId | GenId
32 | 2 | 3
33 | 3 | 1
Attribute_Table
AttribId | GenId | AttribDesc
1 | 1 | Text
2 | 1 | Some Text
2 | 2 | Some Different Text
3 | 1 | Other Text
When I query on a specific object I would like it to return an exact match if possible. For example, Object ID 33 would return "Other Text".
But if there is no exact match, I would like for the most recent generation (largest Gen ID) to be returned. For example, Object ID 32 would return "Some Different Text". Since there is no Attribute ID 2 from Gen 3, it uses the description from the most recent iteration of the Attribute which is Gen ID 2.
This is what I've come up with to accomplish that goal:
SELECT attr.AttribDesc
FROM Attribute_Table AS attr
JOIN Object_Table AS obj
ON obj.AttribId = obj.AttribId
WHERE attr.GenId = (SELECT MIN(GenId)
FROM(SELECT CASE obj2.GenId
WHEN attr2.GenId THEN attr2.GenId
ELSE(SELECT MAX(attr3.GenId)
FROM Attribute_Table AS attr3
JOIN Object_Table AS obj3
ON obj3.AttribId = attr3.AttribId
WHERE obj3.AttribId = 2
)
END AS GenId
FROM Attribute_Table AS attr2
JOIN Object_Table AS obj2
ON attr2.AttribId = obj2.AttribId
WHERE obj2.AttribId = 2
) AS ListOfGens
)
Is there a simpler way to accomplish this? I feel that there should be, but I'm relatively new to SQL and can't think of anything else.
Thanks!
The following query will return the matching value, if found, otherwise use a correlated subquery to return the value with the highest GenId and matching AttribId:
SELECT obj.Object_Id,
CASE WHEN attr1.AttribDesc IS NOT NULL THEN attr1.AttribDesc ELSE attr2.AttribDesc END AS AttribDesc
FROM Object_Table AS obj
LEFT JOIN Attribute_Table AS attr1
ON attr1.AttribId = obj.AttribId AND attr1.GenId = obj.GenId
LEFT JOIN Attribute_Table AS attr2
ON attr2.AttribId = obj.AttribId AND attr2.GenId = (
SELECT max(GenId)
FROM Attribute_Table AS attr3
WHERE attr3.AttribId = obj.AttribId)
In the case where there is no matching record at all with the given AttribId, it will return NULL. If you want to get no record at all in this case, make the second JOIN an INNER JOIN rather than a LEFT JOIN.
Try this...
Incase the logic doesn't find a match for the Object_table GENID it maps it to the next highest GENID in the ON clause of the JOIN.
SELECT AttribDesc
FROM object_TABLE A
INNER JOIN Attribute_Table B
ON A.AttrbId = B.AttrbId
AND (
CASE
WHEN A.Genid <> B.Genid
THEN (
SELECT MAX(C.Genid)
FROM Attribute_Table C
WHERE A.AttrbId = C.AttrbId
)
ELSE A.Genid
END
) -- Selecting the right GENID in the join clause should do the job
= B.Genid
This should work:
with x as (
select *, row_number() over (partition by AttribId order by GenId desc) as rn
from Attribute_Table
)
select isnull(a.attribdesc, x.attribdesc)
from Object_Table o
left join Attribute_Table a
on o.AttribId = a.AttribId and o.GenId = a.GenId
left join x on o.AttribId = x.AttribId and rn = 1

Selecting max value from 2nd table in first table results

I have 2 tables as below-
Table I
ID DATE
1 05/11/12
2 23/11/12
3 29/11/12
4 04/10/12
5 20/11/12
And another table (IH) with the following info-
ID RECNO NOTE
1 1 Open
1 2 Update
1 3 Close
2 1 Open
2 2 Update
2 3 Hold
2 4 Close
3 1 Open
4 1 Open
4 2 Update
5 1 Open
I would like to output a result as shown below, displaying the Note field using the highest value of RecNo for each ID. So using the data above the output should be-
ID DATE NOTE
2 23/11/12 Close
3 29/11/12 Open
The code I have is-
SELECT I.ID, I.DATE, IH.NOTE FROM
I I, IH IH
JOIN (SELECT MAX([RECNO]) [RECNO] FROM
IH
GROUP BY RECNO) IH2 ON IH2.ID = IH.ID AND
IH2.[RECNO] = IH.[RECNO]
JOIN I I2 ON I2.ID = IH.ID WHERE
(I2.DATE>={TS ‘2012-11-22 00:00:002}) GROUP BY I2.ID
However when I execute the code I get-
Invalid Column Name 'RECNO'. Statement(s) could not be prepared.
How about this? Note, haven't tried it, I'm on my Mac at the moment.
SELECT I.ID, I.DATE, IH.NOTE
FROM I I
OUTER APPLY
(SELECT TOP 1 *
FROM IH
WHERE IH.ID = I.ID
ORDER BY RECNO DESC) IH
WHERE I.DATE >= '2012-11-22'
Your SQL is rather, uh, messy.
Assuming you are using SQL Server 2005 or greater, you can use the row_number() function, as follows:
SELECT I.ID, I.DATE, IH.NOTE
FROM I join
(select ih.*, ROW_NUMBER() over (PARTITION by id order by recno desc) as seqnum
from IH
) ih
on IH2.[RECNO] = IH.[RECNO] and seqnum = 1
WHERE I2.DATE>='2012-11-22 00:00:002'
This is assigning a sequence number in the IH table, for each id with the highest record number getting the value "1". The rest is just SQL.
Your original query is simply not correct syntactically, but I think this is what you want based on the description.
and another one
SELECT I.ID, I.DATE
,(Select TOP 1 IH.NOTE FROM IH where IH.ID=i.ID Order by Recno DESC) as Note
from I
WHERE
I.DATE>'20121122'
maybe this will help
SELECT a.ID, a.DATE, b.NOTE FROM a
inner join b on a.ID = b.ID
where b.recno in (select max(bb.recno)
from b as bb where bb.id = b.id)
http://sqlfiddle.com/#!3/fd141/2
If you don't mind the different identifiers, look at this solution:
select t1.MyID, t1.MyDate, y.Note
from t1
join
(
select MyID, max(RecNo) as RecNo
from t2
group by MyID
) x
on t1.MyID = x.MyID
left join
(
select *
from t2
) y
on t1.MyID = y.MyID
and x.RecNo = y.RecNo
where t1.MyDate >= '2012.11.22'
The complete solution is here: http://sqlfiddle.com/#!3/4ca09/3
Update: Oops, forgot to bring in the date in where clause. Updated SQL Fiddle and the query above.

Multiple MAX values select using inner join

I have query that work for me only when values in the StakeValue don't repeat.
Basically, I need to select maximum values from SI_STAKES table with their relations from two other tables grouped by internal type.
SELECT a.StakeValue, b.[StakeName], c.[ProviderName]
FROM SI_STAKES AS a
INNER JOIN SI_STAKESTYPES AS b ON a.[StakeTypeID] = b.[ID]
INNER JOIN SI_PROVIDERS AS c ON a.[ProviderID] = c.[ID] WHERE a.[EventID]=6
AND a.[StakeGroupTypeID]=1
AND a.StakeValue IN
(SELECT MAX(d.StakeValue) FROM SI_STAKES AS d
WHERE d.[EventID]=a.[EventID] AND d.[StakeGroupTypeID]=a.[StakeGroupTypeID]
GROUP BY d.[StakeTypeID])
ORDER BY b.[StakeName], a.[StakeValue] DESC
Results for example must be:
[ID] [MaxValue] [StakeTypeID] [ProviderName]
1 1,5 6 provider1
2 3,75 7 provider2
3 7,6 8 provider3
Thank you for your help
There are two problems to solve here.
1) Finding the max values per type. This will get the Max value per StakeType and make sure that we do the exercise only for the wanted events and group type.
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
2) Then we need to get only one return back for that value since it may be present more then once.
Using the Max Value, we must find a unique row for each I usually do this by getting the Max ID is has the added advantage of getting me the most recent entry.
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
3) Now that we have the ID's of the rows that we want, we can just get that information.
SELECT Stakes.ID, Stakes.StakeValue, SType.StakeName, SProv.ProviderName
FROM SI_STAKES AS Stakes
INNER JOIN SI_STAKESTYPES AS SType ON Stake.[StakeTypeID] = SType.[ID]
INNER JOIN SI_PROVIDERS AS SProv ON Stake.[ProviderID] = SProv.[ID]
WHERE Stake.ID IN (
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
)
You can use the over clause since you're using T-SQL (hopefully 2005+):
select distinct
a.stakevalue,
max(a.stakevalue) over (partition by a.staketypeid) as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
Essentially, this will find the max a.stakevalue for each a.staketypeid. Using a distinct will return one and only one row. Now, if you wanted to include the min a.id along with it, you could use row_number to accomplish this:
select
s.id,
s.maxvalue,
s.staketypeid,
s.providername
from (
select
row_number() over (order by a.stakevalue desc
partition by a.staketypeid) as rownum,
a.id,
a.stakevalue as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
) s
where
s.rownum = 1