Picking one row over another - sql

I have a table that looks like this:
ID Type Value
A Z01 10
A Z09 20
B Z01 30
C Z01 40
D Z09 50
E Z10 60
For each ID I would like to retrieve a value. Ideally the value should come from the row with type Z01. However, if Z01 is not available I'll pick Z09 instead. If nothing is available I would like to select nothing.
The result would look like this:
Id Type Value
A Z01 10
B Z01 30
C Z01 40
D Z09 50
How can I accomplish this with T-SQL?

This should give you what you want:
select *
from table t
where 1 = case
when t.type = 'Z01'
then 1
when t.type = 'Z09'
and not exists (select 1 from table where id = t.id and type = 'Z01')
then 1
else 0
end
An alternative, with using a more common approach is (re-writing the CASE expression):
select *
from table
where type = 'Z01'
OR (type = 'Z09' and not exists (select 1 from table where id = t.id and type = 'Z01'))
An obvious sargable approach (which will make your query use the appropriate index on your table, if it exists) would be:
select *
from table
where type = `Z01`
union all
select *
from table
where type = `Z09`
and not exists (select 1 from table where id = t.id and type = 'Z01')
And when I'm saying index I'm talking about a non-clustered index on the type column.

You can use query like this
;WITH cte
AS (SELECT
*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY type) AS rn
FROM yourtable
WHERE type IN ('Z01', 'Z09'))
SELECT
id, type, value
FROM cte
WHERE rn = 1

I would use window functions:
select t.*
from (select t.*,
row_number() over (partition by id
order by (case when type = 'Z01' then 1
when type = 'Z09' then 2
end)
) as seqnum
from t
) t
where seqnum = 1;
I don't fully understand teh statement "if nothing is available, then choose nothing". If, by this, you mean you want 'Z01', 'Z09' or nothing at all, then just do:
select t.*
from (select t.*,
row_number() over (partition by id order by type) as seqnum
from t
where type in ('Z01', 'Z09')
) t
where seqnum = 1;
The where clause does the filtering, and you can just order by the type alphabetically.

Something like this:
select distinct
base.Id,
Coalesce(z01.Type, any.Type) Type,
Coalesce(z01.Value, any.Value)
from (select distinct id from [table]) base
left outer join [table] z01
on z01.id = base.id
and z01.Type = 'Z01'
left outer join [table] any
on any.id = base.id
and any.type != 'Z01'

Start with a WHERE clause. Select only entries with Z01 and Z09. Then use ROW_NUMBER to rank your rows and keep the best.
select id, type, value
from
(
select
id, type, value,
row_number()
over (partition by id order by case when type = 'Z01' then 1 else 2 end) as rn
from mytable
where type in ('Z01','Z09')
) ranked
where rn = 1;

Then try this
Select distinct a.id,
coalesce(t1.type, t9.type) type,
case when t1.type is not null
then t1.value else t9.value end value
From table a
left join table t1
on t1.id = a.id
and t1.type = 'Z01'
left join table t9
on t9.id = a.id
and t9.type = 'Z09'
Where a.type in ('Z01', 'Z09') -- < -- this last to eliminate row E

Related

SQL Unique ID for union all - Sybase

Is it possible to generate a unique ID for the auxiliary table? I am retrieving data from several tables, but I do not know how to create a new ID for the results:
I would like to have an additional column with ID.
I tried to look for several methods, but nothing helped me.
I will be very grateful.
Greetings,
with ct as (
select *
INTO temp_table
from dba.view_NEW_Users_AreaCodes ur
join dba.view_NEW_Customers_SalesTowns ct on ct.CustSalesTerritoryTTID = ur.UserAreaCodeID
where ur.UserType = 'TT'
and ct.CustSalesTerritoryTTID <> 0
union all
select *
from dba.view_NEW_Users_AreaCodes ur
join dba.view_NEW_Customers_SalesTowns ct on ct.CustSalesTerritoryMTID = ur.UserAreaCodeID
where ur.UserType = 'MT'
and ct.CustSalesTerritoryMTID <> 0
union all
select *
from dba.view_NEW_Users_AreaCodes ur
join dba.view_NEW_Customers_SalesTowns ct on ct.CustSalesTerritoryHRCID = ur.UserAreaCodeID
where ur.UserType = 'HRC'
and ct.CustSalesTerritoryHRCID <> 0
union all
select *
from dba.view_NEW_Users_AreaCodes ur
join dba.view_NEW_Customers_SalesTowns ct on ct.CustSalesTerritoryDevID = ur.UserAreaCodeID
where ur.UserType = 'DEV'
and ct.CustSalesTerritoryDevID <> 0
)
select row_number() over (order by newid()) as DATA_ID,
ct.*
from ct;
You could use row_number():
with t as (
< your query here >
)
select row_number() over (order by newid()) as seqnum,
t.*
from t;
newid() is just an arbitrary value that randomizes the numbering. You can use a column there if you prefer a more canonical ordering.

SQL: How do I ignore duplicates in a select statement while preferring one type of entry over another?

Each entry has an ID (random string of numbers and letters), a Name (string), and a type (string "A" or "B").
Some entries share the same ID and Name, but have different types.
I'm trying to write a select statement that ignores entries of type B when there is an entry using the same ID of type A.
As far as I understand, DISTINCT wont work as it relies on the elements matching in all columns, and can not differentiate based on a column.
Here's one way...
with type_a as
(select distinct id, name, type
from table_name
where type = 'A'
),
type_b as
(select distinct id, name, type
from table_name
where type = 'B'
and id not in (select id from type_a)
)
select * from type_a
union
select * from type_b
Use NOT EXISTS:
select t.*
from tablename t
where t.type = 'A'
or not exists (select 1 from tablename where id = t.id and name = t.name and type = 'A')
If the name should not be involved in the condition, then use this:
or not exists (select 1 from tablename where id = t.id and type = 'A')
Or use RANK() window function:
select t.id, t.name, t.type
from (
select t.*
rank() over (partition by id, name order by case when type = 'A' then 1 else 2 end) rnk
from tablename
) t
where t.rnk = 1
Or remove name from partition if it is not relevant.

SQL Get rows based on conditions

I'm currently having trouble writing the business logic to get rows from a table with id's and a flag which I have appended to it.
For example,
id: id seq num: flag: Date:
A 1 N ..
A 2 N ..
A 3 N
A 4 Y
B 1 N
B 2 Y
B 3 N
C 1 N
C 2 N
The end result I'm trying to achieve is that:
For each unique ID I just want to retrieve one row with the condition for that row being that
If the flag was a "Y" then return that row.
Else return the last "N" row.
Another thing to note is that the 'Y' flag is not always necessarily the last
I've been trying to get a case condition using a partition like
OVER (PARTITION BY A."ID" ORDER BY A."Seq num") but so far no luck.
-- EDIT:
From the table, the sample result would be:
id: id seq num: flag: date:
A 4 Y ..
B 2 Y ..
C 2 N ..
Using a window clause is the right idea. You should partition the results by the ID (as you've done), and order them so the Y flag rows come first, then all the N flag rows in descending date order, and pick the first for each id:
SELECT id, id_seq_num, flag, date
FROM (SELECT id, id_seq_num, flag, date,
ROW_NUMBER() OVER (PARTITION BY id
ORDER BY CASE flag WHEN 'Y' THEN 0
ELSE 1
END ASC,
date ASC) AS rk
FROM mytable) t
WHERE rk = 1
My approach is to take a UNION of two queries. The first query simply selects all Yes records, assuming that Yes only appears once per ID group. The second query targets only those ID having no Yes anywhere. For those records, we use the row number to select the most recent No record.
WITH cte1 AS (
SELECT id
FROM yourTable
GROUP BY id
HAVING SUM(CASE WHEN flag = 'Y' THEN 1 ELSE 0 END) = 0
),
cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY t1.id ORDER BY t1."id seq" DESC) rn
FROM yourTable t1
INNER JOIN cte1 t2
ON t1.id = t2.id
)
SELECT *
FROM yourTable
WHERE flag = 'Y'
UNION ALL
SELECT *
FROM cte2 t2
WHERE t2.rn = 1
Here's one way (with quite generic SQL):
select t1.*
from Table1 as t1
where t1.id_seq_num = COALESCE(
(select max(id_seq_num) from Table1 as T2 where t1.id = t2.id and t2.flag = 'Y') ,
(select max(id_seq_num) from Table1 as T3 where t1.id = t3.id and t3.flag = 'N') )
Available in a fiddle here: http://sqlfiddle.com/#!9/5f7f9/6
SELECT DISTINCT id, flag
FROM yourTable

SQL Case depending on previous status of record

I have a table containing status of a records. Something like this:
ID STATUS TIMESTAMP
1 I 01-01-2016
1 A 01-03-2016
1 P 01-04-2016
2 I 01-01-2016
2 P 01-02-2016
3 P 01-01-2016
I want to make a case where I take the newest version of each row, and for all P that has at some point been an I, they should be cased as a 'G' instead of P.
When I try to do something like
Select case when ID in (select ID from TABLE where ID = 'I') else ID END as status)
From TABLE
where ID in (select max(ID) from TABLE)
I get an error that this isn't possible using IN when casing.
So my question is, how do I do it then?
Want to end up with:
ID STATUS TIMESTAMP
1 G 01-04-2016
2 G 01-02-2016
3 P 01-01-2016
DBMS is IBM DB2
Have a derived table which returns each id with its newest timestamp. Join with that result:
select t1.ID, t1.STATUS, t1.TIMESTAMP
from tablename t1
join (select id, max(timestamp) as max_timestamp
from tablename
group by id) t2
ON t1.id = t2.id and t1.TIMESTAMP = t2.max_timestamp
Will return both rows in case of a tie (two rows with same newest timestamp.)
Note that ANSI SQL has TIMESTAMP as reserved word, so you may need to delimit it as "TIMESTAMP".
You can do this by using a common table expression find all IDs that have had a status of 'I', and then using an outer join with your table to determine which IDs have had a status of 'I' at some point.
To get the final result (with only the newest record) you can use the row_number() OLAP function and select only the "newest" record (this is shown in the ranked common table expression below:
with irecs (ID) as (
select distinct
ID
from
TABLE
where
status = 'I'
),
ranked as (
select
rownumber() over (partition by t.ID order by t.timestamp desc) as rn,
t.id,
case when i.id is null then t.status else 'G' end as status,
t.timestamp
from
TABLE t
left outer join irecs i
on t.id = i.id
)
select
id,
status,
timestamp
from
ranked
where
rn = 1;
other solution
with youtableranked as (
select f1.id,
case (select count(*) from yourtable f2 where f2.ID=f1.ID and f2."TIMESTAMP"<f1."TIMESTAMP" and f2.STATUS='I')>0 then 'G' else f1.STATUS end as STATUS,
rownumber() over(partition by f1.id order by f1.TIMESTAMP desc, rrn(f1) desc) rang,
f1."TIMESTAMP"
from yourtable f1
)
select * from youtableranked f0
where f0.rang=1
ANSI SQL has TIMESTAMP as reserved word, so you may need to delimit it as "TIMESTAMP"
try this
select distinct f1.id, f4.*
from yourtable f1
inner join lateral
(
select
case (select count(*) from yourtable f3 where f3.ID=f2.ID and f3."TIMESTAMP"<f2."TIMESTAMP" and f3.STATUS='I')>0 then 'G' else f2.STATUS end as STATUS,
f2."TIMESTAMP"
from yourtable f2 where f2.ID=f3.ID
order by f2."TIMESTAMP" desc, rrn(f2) desc
fetch first rows only
) f4 on 1=1
rrn(f2) order is for same last date
ANSI SQL has TIMESTAMP as reserved word, so you may need to delimit it as "TIMESTAMP"

SQL Joining table with Min and Sec Min row

I want to join table 1 with table2 twice becuase I need to get the first minimum record and the second minimum. However, I can only think of using a cte to get the second minimum record. Is there a better way to do it?
Here is the table table:
I want to join Member with output table FirstRunID whose Output value is 1 and second RunID whose Output value is 0
current code I am using:
select memid, a.runid as aRunid,b.runid as bRunid
into #temp
from FirstTable m inner join
(select min(RunID), MemID [SecondTable] where ouput=1 group by memid)a on m.memid=a.memid
inner join (select RunID, MemID [SecondTable] where ouput=0 )b on m.memid=a.memid and b.runid>a.runid
with cte as
(
select row_number() over(partition by memid, arunid order by brunid ),* from #temp
)
select * from cte where n=1
You can use outer apply operator for this:
select * from t1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 1 order by t2.runid) as oa1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 0 order by t2.runid) as oa2
You can do this with conditional aggregation. Based on your results, you don't need the first table:
select t2.memid,
max(case when output = 1 and seqnum = 1 then runid end) as OutputValue1,
max(case when output = 0 and seqnum = 2 then runid end) as OutputValue2
from (select t2.*,
row_number() over (partition by memid, output order by runid) a seqnum
from t2
) t2
group by t2.memid;
declare #FirstTable table
(memid int, name varchar(20))
insert into #firsttable
values
(1,'John'),
(2,'Victor')
declare #secondtable table
(runid int,memid int,output int)
insert into #secondtable
values
(1,1,0),(1,2,1),(2,1,1),(2,2,1),(3,1,1),(3,2,0),(4,1,0),(4,2,0)
;with cte as
(
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 1
union all
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 0 and
t.runid > (select min(x.runid) from #secondtable x where x.memid = t.memid and x.output = 1 group by x.memid) --lose any O output record where there is no prior 1 output record
)
select cte1.memid,cte1.runid,cte2.runid from cte cte1
join cte cte2 on cte2.memid = cte1.memid and cte2.seq = cte1.seq
where cte1.seq = 1 --remove this test if you want matched pairs
and cte1.output = 1 and cte2.output = 0