Use Row_Number() in case statement in sql server - sql

I Left Joined 2 table Table A and Table B and the output is below which is correct
select a.id,b.OS_ID from TableA left join TableB
on a.id=b.id
What I want to achieve is replacing NULL with 51 and 52.
If I run this
Declare #OSID int
set #OSID = (select max(os_ID) from OS_Master)
It will give me output as 50, Then I want to increment by 1 for the next entry and replace the NULL value
ID OS_ID
1 1
1 14
1 NULL
1 NULL
If I do this:
Declare #OSID int
set #OSID = (select max(os_ID) from OS_Master)
select ROW_NUMBER() over (order by os_ID) + #OSID from OS_Master
It works fine, ROW_NUMBER() starts from 51, But If I incorporate it with the join like
select a.ID,case when b.OS_ID is null then (select ROW_NUMBER() over (order by b.os_ID) + #OSID )
else b.OS_ID END from TABLEA a
left JOIN TABLEB b
on a.ID=b.ID
It shows 51 for both the NULL records, How to get 51,52 ans so on in my query

Is this what you want?
select a.id,
coalesce(b.OS_ID, om.max_os_id + row_number() over (partition by b.os_id)) as os_id
from TableA left join
TableB
on a.id = b.id cross join
(select max(os_ID) as max_os_id from OS_Master) om;
This increments the value based on the maximum value, using row_number().

Related

Using Partition By in an inner join to return a single value

I have read a table and it returned a value. I now need to do an Inner Join with another table to get the next highest value available after this value in this Table.
I.e I have just returned the value 7 from a select and the Table I now need to join with contains the values;
1
5
7
11
20
I only want to return 11 in my join.
I have tried 'Row_Number () Over Partition By' but doesn't work for me because I am using an 'on (A.Number > B.Number) in the Join statement so the Row Number returned will not always be 1 for me.
Any advice?
I tried something like this;
SELECT a_number_field
FROM Table_A A
INNER JOIN (
SELECT ROW_NUMBER() OVER (
PARTITION BY another_number_field
ORDER BY another_number_field
) rn
FROM Table_B
) B
ON (b.another_number_field > a.A_number_field)
WHERE a.number_field = 7
and rn=1;
I am expecting only the value 11 to be returned to me.
Use the ROW_NUMBER with your join query as the following:
SELECT a_number_field, another_number_field
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY A.a_number_field ORDER BY another_number_field) rn FROM
tableA A JOIN tableB B
ON B.another_number_field > A.a_number_field
) T
WHERE rn = 1 AND a_number_field = 7
See a demo.

How to select groups which has only the values we want and not select it if it also has other values in SQL

id
code
value
A
cod
2
A
buy
34
A
cod
4
B
cod
44
B
F
23
C
thk
45
C
cod
33
C
F
31
D
cod
22
In this table for example, I want those groups of id which has 'code' column value as ONLY cod or F. so query should return values of id = B and nothing else. ( Not even values with id = C because id=C also has 'thk' in code , not even id= D, and output should have ids with ONLY the mentioned two values)
expected output
id
code
value
B
cod
44
B
F
23
You want all rows for the ID of which not exists a forbidden row:
select id, code, value
from mytable
where not exists
(
select null
from mytable forbidden_row
where forbidden_row.id = mytable.id
and forbidden_row.code not in ('cod', 'F')
);
One of approaches with nested query
SELECT ID,Code, value FROM (
select ID, Code,
(SELECT count(*) FROM TableA a where Code = 'cod' and a.ID = TableA.ID) Cod,
(SELECT count(*) FROM TableA a where Code = 'F' and a.ID = TableA.ID) F,
(SELECT count(*) FROM TableA a where Code not in ('F','cod') and a.ID = TableA.ID) Other,
Value
from TableA
) SOURCE
WHERE Cod <> 0 AND F <> 0 and Other = 0
We can achieve this using CTE. Check this,
-- Split the two record category first, then check cod Or F condition.
WITH Count2 AS (
SELECT id
FROM YourTable
GROUP BY id
HAVING COUNT(id) = 2
),
codORF AS (
SELECT id, code, COUNT(id) FROM YourTable T1
LEFT JOIN Count2 T2 On T1.id = T2.id
WHERE code = 'cod' OR code = 'F'
GROUP BY id, code
Having COUNT(id) = 1
)
-- Finally to take all values
SELECT T1.*
FROM YourTable T1
INNER JOIN codORF T2 ON T1.id = T2.id
with main as (
select *, count(id) over(partition by id order by id) as total_rows
from sample
), next_and_before as (
select *,
COALESCE(lag(code) over(partition by id order by id),lead(code) over(partition by id order by id)) as before_next
from main where total_rows <= 2
)
select * from next_and_before
where lower(trim(concat(code,before_next)))in('codf','fcod','cod','f')
Its a bit of hacky solution:
first you are filtering out all the rows that have less than or equal to 2 rows, since there could be cases where you only have one row per id with a code value = 'f' or 'cod', if you don't want that then simply change the last part to: in ('codf','fcod')
then out of two rows, you are looking at the next and before value and checking if it contains other than 'f' or 'cod'
where clause will filter those out if they exist
Test Results from the link below:
Results of sample data

How to get the records of table A, which are in one row in table B

SELECT ClaimID, CPTCode FROM TABLEA
ClaimId CPTCode
**60 62000**
**60 0213T**
60 99383
60 93230
60 96372
SELECT cpt1,CPT2 FROM TABLEB
cpt1 CPT2
**62000 0213T**
**62000 0230T**
62000 0216T
62000 0228T
SELECT the record from tableA only that which is at same row in tableB
Result should be
60 62000
60 0213T
I think this does what you want:
select ClaimID, CPTCode
from tablea a
where exists (select 1
from tableb b
where b.cpt1 = a.cptcode
) or
exists (select 1
from tableb b
where b.cpt2 = a.cptcode
);
This query can take advantage of two indexes: tableb(cpt1) and tableb(cpt2).
You can write this as:
select ClaimID, CPTCode
from tablea a
where exists (select 1
from tableb b
where a.cptcode in (b.cpt1, b.cpt2)
);
However, this version is much harder to optimize.
try this-
select obj.ClaimID, obj.CPTCode from (
select row_number() as row_noA, ClaimID, CPTCode FROM TABLEA
join
select row_number() as row_noB, cpt1,CPT2 FROM TABLEB
on TABLEA.CPTCode = TABLEB.CPT2 and TABLEA.row_noA = TABLEB.row_noB
)obj
join two tables and match each row using same row number and get the output

SQL: Select lowest value that doesn't already exist

In TableA I have an int column.
Is it possible using only a select statement to select the minimum value in the column that DOES NOT EXIST and is greater then 0?
For example, if the col has the values 1,2,9 the select statement will return 3.
If the col has 9,10,11 it will return 1.
I can achieve this using a temp table or using a loop, but I'm wondering if I can do it using just a select statement?
SELECT MIN(t1.ID+1) as 'MinID'
FROM table t1 LEFT JOIN table t2
On t1.ID+1=t2.ID
Where t2.OtherField IS NULL
select
min(nt.id)
from numbertable nt
left outer join originaldata od
on nt.id=od.id
where od.id is null
have a number table that goes from 1 to your max value (or higher)
SELECT DISTINCT x + 1 "val"
EXCEPT SELECT DISTINCT x "val"
ORDER BY "val" ASC
LIMIT 1
What about this?
SELECT Min(id)
FROM (SELECT 1 id
FROM tablea
WHERE 1 NOT IN (SELECT id
FROM tablea)
UNION
SELECT id + 1 id
FROM tablea
WHERE id + 1 NOT IN (SELECT id
FROM tablea)) AS min_ids;
try this:(Updated)
declare #dummy varchar(10) ;
set #dummy =(select top(1) id from dbo.b)
if( #dummy= '1')
begin
select top(1)l.id + 1 as start
from dbo.b as l
left outer join dbo.b as r on l.id + 1 = r.id
where r.id is null
end
else
begin
select '1'
end
Give this a try:
declare #TestTable table (
col int
)
/* Test Case 1: 1,2,9 */
insert into #TestTable
(col)
select 1 union all select 2 union all select 9
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
delete from #TestTable
/* Test Case 2: 9,10,11 */
insert into #TestTable
(col)
select 9 union all select 10 union all select 11
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
I duplicated my answer from here:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table UNION SELECT 0) a
LEFT JOIN table b ON b.id = a.id + 1
WHERE b.id IS NULL
This handles all cases I can think of - including no existing records at all.
The only thing I don't like about this solution is that additional conditions have to be included twice, like that:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table WHERE column = 4711 UNION SELECT 0) a
LEFT JOIN table b ON b.column = 4711 AND b.id = a.id + 1
WHERE b.id IS NULL
Please also notice the comments about locking and concurrency - the requirement to fill gaps is in most cases bad design and can cause problems. However, I had a good reason to do it: the IDs are to be printed and typed by humans and we don't want to have IDs with many digits after some time, while all the low ones are free...

Problem combining result of two different queries into one

I have two tables (TableA and TableB).
create table TableA
(A int null)
create table TableB
(B int null)
insert into TableA
(A) values (1)
insert into TableB
(B) values (2)
I cant join them together but still I would like to show the result from them as one row.
Now I can make select like this:
select
(select A from tableA) as A
, B from TableB
Result:
A B
1 2
But if I now delete from tableB:
delete tableB
Now when I run the same query as before:
select
(select A from tableA) as A
, B from TableB
I see this:
A B
But I was expecting seeing value from tableA
like this:
Expected Result:
A B
1
Why is this happening and how can I still see the value from TableA although selectB is returning 0 rows?
I am using MS SQL Server 2005.
Use a LEFT JOIN (although it's more of a cross join in your case).
If your db supports it:
SELECT a.a, b.b
FROM a
CROSS JOIN b
If not, do something like:
SELECT a.a, b.b
FROM a
LEFT JOIN b ON ( 1=1 )
However, once you have more rows in a or b, this will return the cartesian product:
1 1
1 2
2 1
2 2
This will actually give you what you're looking for, but if you only have one row per table:
select
(select A from tableA) as A
, (select B from TableB) as B
give this a try:
DECLARE #TableA table (A int null)
DECLARE #TableB table (B int null)
insert into #TableA (A) values (1)
insert into #TableB (B) values (2)
--this assumes that you don't have a Numbers table, and generates one on the fly with up to 500 rows, you can increase or decrease as necessary, or just join in your Numbers table instead
;WITH Digits AS
(
SELECT 0 AS nbr
UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
)
, AllNumbers AS
(
SELECT u3.nbr * 100 + u2.nbr * 10 + u1.nbr + 1 AS Number
FROM Digits u1, Digits u2, Digits u3
WHERE u3.nbr * 100 + u2.nbr * 10 + u1.nbr + 1 <= 500
)
, AllRowsA AS
(
SELECT
A, ROW_NUMBER() OVER (ORDER BY A) AS RowNumber
FROM #TableA
)
, AllRowsB AS
(
SELECT
B, ROW_NUMBER() OVER (ORDER BY B) AS RowNumber
FROM #TableB
)
SELECT
a.A,b.B
FROM AllNumbers n
LEFT OUTER JOIN AllRowsA a on n.Number=a.RowNumber
LEFT OUTER JOIN AllRowsB b on n.Number=b.RowNumber
WHERE a.A IS NOT NULL OR b.B IS NOT NULL
OUTPUT:
A B
----------- -----------
1 2
(1 row(s) affected)
if you DELETE #TableB, the output is:
A B
----------- -----------
1 NULL
(1 row(s) affected)
try this:
select a, (select b from b) from a
union
select b, (select a from a) from b
should retrieve you all the existing data.
you can filter it more by surrounding it with another select