SQL SERVER Replace Null - sql

I have a tsql query like that:
DECLARE #TABLE TABLE(
id int primary key identity(1,1),
code int );
INSERT INTO #TABLE VALUES (1),(NULL),(NULL),(NULL),(2),(NULL),(NULL),(NULL),(3),(NULL),(NULL),(NULL);
SELECT
id ,
code
FROM #TABLE T1
My result :
id code
1 1
2 NULL
3 NULL
4 NULL
5 2
6 NULL
7 NULL
8 NULL
9 3
10 NULL
11 NULL
12 NULL
I want to change null value with null value's last one numeric value. I want to see that:
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 2
9 3
10 3
11 3
12 3
But i dont want to use while loop. How can i do it ?

If the values are increasing, use a cumulative max:
select t.*,
max(code) over (order by id) as imputed_code
from #table t;
If the code is not strictly increasing, then you can do this in two steps:
select t.*, max(code) over (order by grp) as imputed_code
from (select t.*,
count(code) over (order by id) as grp
from #table t
) t;
Given that this is a table variable, I'm guessing that you don't really want to update it. But if you do:
with toupdate as (
<one of the above queries>
)
update toupdate
set code = imputed_code
where code is null;

Related

T-SQL Select all combinations of ranges that meet aggregate criteria

Problem restated per comments
Say we have the following integer id's and counts...
id count
1 0
2 10
3 0
4 0
5 0
6 1
7 9
8 0
We also have a variable #id_range int.
Given a value for #id_range, how can we select all combinations of id ranges, without using while loops or cursors, that meet the following criteria?
1) No two ranges in a combination can overlap (min and max of each range are inclusive)
2) sum(count) for a combination of ranges must equal sum(count) of the initial data set (20 in this case)
3) Only include ranges where sum(count) > 0
The simplest case would be when #id_range = max(id) - min(id), or 7 given the above data. In this case, there's only one solution:
minId maxId count
---------------------
1 8 20
But if #id_range = 1 for example, there would be 4 possible solutions:
Solution 1:
minId maxId count
---------------------
1 2 10
5 6 1
7 8 9
Solution 2:
minId maxId count
---------------------
1 2 10
6 7 10
Solution 3:
minId maxId count
---------------------
2 3 10
5 6 1
7 8 9
Solution 4:
minId maxId count
---------------------
2 3 10
6 7 10
The end goal is to identify which solutions have the fewest number of ranges (solution # 2 and 4, in above example where #id_range = 1).
this solution does not list all possible combination but just try to get group it in smallest possible no of rows.
Hopefully it will cover all possible scenario
-- create the sample table
declare #sample table
(
id int,
[count] int
)
-- insert some sample data
insert into #sample select 1, 0
insert into #sample select 2, 10
insert into #sample select 3, 0
insert into #sample select 4, 0
insert into #sample select 5, 0
insert into #sample select 6, 1
insert into #sample select 7, 9
insert into #sample select 8, 0
-- the #id_range
declare #id_range int = 1
-- the query
; with
cte as
(
-- this cte identified those rows with count > 0 and group them together
-- sign(0) gives 0, sign(+value) gives 1
-- basically it is same as case when [count] > 0 then 1 else 0 end
select *,
grp = row_number() over (order by id)
- dense_rank() over(order by sign([count]), id)
from #sample
),
cte2 as
(
-- for each grp in cte, assign a sub group (grp2). each sub group
-- contains #id_range number of rows
select *,
grp2 = (row_number() over (partition by grp order by id) - 1)
/ (#id_range + 1)
from cte
where count > 0
)
select MinId = min(id),
MaxId = min(id) + #id_range,
[count] = sum(count)
from cte2
group by grp, grp2

SQL to get next not null value in column

How can I get next not null value in column? I have MSSQL 2012 and table with only one column. Like this:
rownum Orig
------ ----
1 NULL
2 NULL
3 9
4 NULL
5 7
6 4
7 NULL
8 9
and I need this data:
Rownum Orig New
------ ---- ----
1 NULL 9
2 NULL 9
3 9 9
4 NULL 7
5 7 7
6 4 4
7 NULL 5
8 9 5
Code to start:
declare #t table (rownum int, orig int);
insert into #t values (1,NULL),(2,NULL),(3,9),(4,NULL),(5,7),(6,4),(7,NULL),(8,9);
select rownum, orig from #t;
One method is to use outer apply:
select t.*, t2.orig as newval
from #t t outer apply
(select top 1 t2.*
from #t t2
where t2.id >= t.id and t2.orig is not null
order by t2.id
) t2;
One way you can do this with window functions (in SQL Server 2012+) is to use a cumulative max on id, in inverse order:
select t.*, max(orig) over (partition by nextid) as newval
from (select t.*,
min(case when orig is not null then id end) over (order by id desc) as nextid
from #t
) t;
The subquery gets the value of the next non-NULL id. The outer query then spreads the orig value over all the rows with the same id (remember, in a group of rows with the same nextid, only one will have a non-NULL value for orig).

Sequencing and re-setting in SQL Server 2008

I am actually new to SQL server 2008, and I am trying to sequence and re-set a number in a table. The source is something like:
Row Refrec FLAG
1 5 NULL
2 4 X
3 3 NULL
4 2 NULL
5 1 Y
6 5 A
7 4 B
8 3 NULL
9 2 NULL
10 1 NULL
The result should look like:
Row Refrec FLAG SEQUENCE
1 5 NULL NULL
2 4 X 0
3 3 NULL 1
4 2 NULL 2
5 1 Y 0
6 5 A 0
7 4 B 0
8 3 NULL 1
9 2 NULL 2
10 1 NULL 3
Thanks!
It looks like you want to enumerate the sequence values for NULL values, setting all the other values to 0. I'm not sure why the first value is NULL, but that is easily fixed.
The following may do what you want:
select t.*,
(case when flag is not null then 0
else row_number() over (partition by seqnum - row order by row)
end) as Sequence
from (select t.*, row_number() over (partition by flag order by row) as seqnum
from table t
);
If you really care about the first value:
select t.*,
(case when row = 1 then NULL
when flag is not null then 0
else row_number() over (partition by seqnum - row order by row)
end) as Sequence
from (select t.*, row_number() over (partition by flag order by row) as seqnum
from table t
);

SQL query for excluding a key value matching record for a specific condition

I've a table as below :
declare #temp table(
PkId int,
DetailId int,
Type int
)
insert into #temp(PkId,DetailId,[Type])
select 1,1,5
union
select 2,1,3
union
select 3,1,4
union
select 4,2,5
union
select 5,3,5
union
select 6,3,3
select * from #temp order by DetailId
returns me
PkId DetailId TypeID
1 1 5
2 1 3
3 1 4
4 2 5
5 3 5
6 3 3
Conditions for getting the records are
For the given 'DetaildID' if only TypeID 5 is present, shall return 5
If 3 or 4 are present then exclude 5
I'm expecting the output as
2 1 3
3 1 4
4 2 5
6 3 3
Please help me with a query.
I don't understand the meaning of the rules, if not some sql puzzle, but it's possible to use the windowing function of SQLServer 2008 to write them
WITH C AS (
SELECT pkId, DetailId, typeID
, _34 = SUM(CASE WHEN TypeID IN (3, 4) THEN 1 ELSE 0 END)
OVER (PARTITION BY DetailId)
, _5 = SUM(TypeID) OVER (PARTITION BY DetailId)
FROM Table1
)
SELECT pkId, DetailId, typeID
FROM C
WHERE (_34 > 0 AND TypeID <> 5)
OR (_5 = 5)
SQLFiddle demo
For every row of a DetaildID group:
_34 is positive if there is a TypeID 3 or a TypeID 4 in the group
_5 will be 5 if the only TypeID in the group is 5
Those value are used in the WHERE condition of the main query to filter the data. The second condition (_5 = 5) don't check for the value of _34 as it's already implicit.
There should probably be a fallback condition in case TypeID has a value different from 3, 4 or 5, the query as it is will return them in a group with 3 or 4 (_34 > 0 AND TypeID <> 5) and remove it otherwise (_34 = 0 AND _5 <> 5).
select pkid, detailid, type
from temp
where type <> 5
group by pkid, detailid, type
union
select pkid, detailid, type
from temp
where detailid not in (
select detailid
from temp
where type <> 5
group by pkid, detailid, type
)
order by pkid
TEST

The row must be shown same order in result set according to column value

I am using SQL Server 2008 R2.
I have the following data:
ID Value OrderNumber
1 A NULL
2 E 4
3 C NULL
4 B NULL
5 F 2
6 D NULL
i want to write a query that must fetch data ordering by OrderNumber column considering OrderNumber values. The query result must be below:
ID Value OrderNumber
1 A NULL
5 F 2 --indicates row must be second in result set.
3 C NULL
2 E 4 --indicates row must be fourth in result set.
4 B NULL
6 D NULL
Thanks for reading and your answers.
I've tried a number of different ways, but the only way I can find that produces the required results in a guaranteed way is:
declare #t table (ID int not null,Value char(1) not null,OrderNumber int null)
insert into #T(ID,Value,OrderNumber) values
(1,'A',NULL),
(2,'E',4),
(3,'C',NULL),
(4,'B',NULL),
(5,'F',2),
(6,'D',NULL)
;With Nbrs as (
select ROW_NUMBER() OVER (ORDER BY ID) as n from #t
), AvailableNbrs as (
select n,ROW_NUMBER() OVER (ORDER BY n) as rn from Nbrs where n not in (select OrderNumber from #t where OrderNumber is not null)
), RequiredOrders as (
select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from #t where OrderNumber is null
)
select
*,COALESCE(OrderNumber,an.n) as FinalOrder
from
#t t
left join
RequiredOrders ro
on
t.ID = ro.ID
left join
AvailableNbrs an
on
ro.rn = an.rn
order by COALESCE(OrderNumber,an.n)
Where we use a few CTEs to find OrderNumbers that aren't currently assigned, and to match those 1-1 with rows which have no OrderNumber.
Results:
|--------- #t --------------| |----- RequiredOrders ---------| |----- AvailableNbrs -------------------| |- COALESCE -------|
ID Value OrderNumber ID rn n rn FinalOrder
----------- ----- ----------- ----------- -------------------- -------------------- -------------------- --------------------
1 A NULL 1 1 1 1 1
5 F 2 NULL NULL NULL NULL 2
3 C NULL 3 2 3 2 3
2 E 4 NULL NULL NULL NULL 4
4 B NULL 4 3 5 3 5
6 D NULL 6 4 6 4 6
Found a much better solution than the accepted answer:
declare #t table(id int, value char, ordernumber int)
insert #t values(1,'A', null)
insert #t values(2,'E',4)
insert #t values(3,'C',NULL)
insert #t values(4,'B',NULL)
insert #t values(5,'F',2)
insert #t values(6,'D',NULL)
;with a as
(
select *, row_number() over (order by id)+.1 rn1 from #t
where ordernumber is null
union all
select *, ordernumber - rank() over (order by ordernumber)+1 rn1 from #t
where ordernumber is not null
)
select * from a order by rn1, ordernumber
Try this:
SELECT *
FROM tableName
Order BY CASE WHEN OrderNumber IS NULL THEN ID ELSE OrderNumber END