Check for null and replace the most recent not null value - sql

The Query needs to Check for null value and replace with the most recent not null value.
For example consider the source data provided below .
Query needs to check the null value and found it in second row, now it should replace the null value with recent not null data and the data is"colmin".
Again the Query need to check the null value and Now it found it in third row and then it will update with recent not null data and the data is again "colmin".
Thanks in advance !
Input
source data
Colmin
NULL
NULL
NULL
NULL
columnxy
column99
NULL
NULL
money
NULL
NULL
NULL
start end
NULL
so the output should look like..
output
Ouput data
Colmin
Colmin
Colmin
Colmin
Colmin
Colmin
columnxy
column99
column99
column99
money
money
money
money
start end
start end

What is "most recent" ? I hope you've got some field to sort on. Row number MUST NOT ALWAYS be in right order! Though I'd use orderField which is will be used to determine the row order.
UPDATE myTable
SET a = (
SELECT a
FROM myTable
WHERE a IS NOT NULL
AND orderField > (
SELECT orderField
FROM myTable
WHERE a IS NULL
ORDER BY orderField
LIMIT 1
)
ORDER BY orderField
LIMIT 1
)
WHERE a IS NULL
ORDER BY orderField
something like this should do it ... i hope. it's untested.
what it does:
Find "orderField" for first row with a = null
Find first a value (!= null) AFTER orderField from 1.
Update a value from 1. with value from 2.
It should also work more easy:
UPDATE myTable t1
SET t1.a = (
SELECT t2.a
FROM myTable t2
WHERE t2.a IS NOT NULL
AND t2.orderField > t1.orderField
ORDER BY t2.orderField
LIMIT 1
)
WHERE t1.a IS NULL

Try this:
declare #input table (id bigint not null identity(1,1), OutputData nvarchar(16) null)
insert #input select 'Colmin'
insert #input select NULL
insert #input select NULL
insert #input select NULL
insert #input select NULL
insert #input select 'columnxy'
insert #input select 'column99'
insert #input select NULL
insert #input select NULL
insert #input select 'money'
insert #input select NULL
insert #input select NULL
insert #input select NULL
insert #input select 'start end'
insert #input select NULL
--where a's value is null, replace with a non-null from b
select coalesce(a.OutputData, b.OutputData)
--return all rows from input as a
from #input a
--return all non-null values as b
left outer join
(
select id, OutputData
from #input
where OutputData is not null
) b
--the non-null value should come before the null value (< or <= works equally here)
on b.id <= a.id
--and there shouldn't be a non-null value between b and a's records (i.e. b is the most recent before a)
and not exists (
select top 1 1
from #input c
where c.id between b.id and a.id
and c.id <> b.id
and c.OutputData is not null
)
--display the output in the required order
order by a.id

If your table is a single column, then you will need to either run multiple queries in a loop or use a cursor to iterate through the table, one row at a time. (not efficient.)
If you have some kind of ID column, then you can use a correlated subquery to find the first non-null value. Something like...
Update A
Set TextCol = (SELECT Top 1 TextCol From MyTable B Where B.TextCol IS NOT NULL AND B.IDCol < A.IDCol ORDER BY IDCol DESC)
FROM MyTable A
WHERE TextCol IS NULL

Related

SQL - update query - update to next date value that is not NULL

I have a bunch of values that are currently on dates with NULL value (i.e. no data available on those particular dates).
How would I go about updating those values to the next date where there is data available?
I have a select query currently which highlights all values that lie on a date with NULL value (or false data defined by a value of less than 0):
select * from table1 a
left join table2 b on a.id=b.id and a.date=b.date --joins dates table to main data set
where a.id in (select c.id from table3 c
left join table4 d on c.id=d.id where c.value = 000000) -- sub query identifying sub set of data I want to use as 'id' list
and a.date is not NULL and a.date > '1900-01-01' --a.date not NULL just identifies illegitimate date values that I don't want to see
and (b.value is NULL or b.value < 0) --identifies legitimate values that fall on dates where there are NULL values or false dates
So this query gives me all values from a chosen data set that fall on dates with false data or NULL values. There are a few more 'where' and 'and' variables I've used in the query but this hopefully gives a good base of understanding.
I would like to update all of these values to the next date in the future that is not NULL (i.e. has legit data).
Just a small example of what I'm thinking: update table1 set date = (assume there would be some sort of select sub query here to define next date value that is not NULL).
Just another note to take into consideration: the next date that the value is not NULL is dynamic - it could be 2 days from given date but it could be 2 years.
/*I would create a variable table #mytab in which I will put sample sample data
with dates and null*/
--Kamel Gazzah
--07/03/2019
declare #mytab as table(id int identity(1,1),mydate date)
insert into #mytab values('01/01/2018')
insert into #mytab values(NULL)
insert into #mytab values('01/05/2018')
insert into #mytab values('01/07/2018')
insert into #mytab values('01/08/2018')
insert into #mytab values(NULL)
insert into #mytab values(NULL)
insert into #mytab values(NULL)
insert into #mytab values('01/08/2018')
select * from #mytab
--First Method with **OUTER APPLY**
update t1 set mydate=t2.mydate
--select t1.*,t2.mydate
from #mytab t1
OUTER APPLY (select top 1 * from #mytab where mydate is not null and id > t1.id order by mydate) t2
where t1.mydate is null
--SCOND METHOD WITH **LEFT OUTER JOIN**
update ta set mydate=tc.mydate
--select ta.id,tc.mydate
from #mytab ta
inner join(
select id1,min(id2) id2 from(
select t1.id id1,t2.id id2,t2.mydate from #mytab t1
left outer join #mytab t2 on t2.id > t1.id and t2.mydate is not null
where t1.mydate is null) v group by id1) tb on ta.id=id1
inner join #mytab tc on tb.id2=tc.id
select * from #mytab
You can solve it using apply
UPDATE T
SET Date = N.Date
FROM yourTable T
OUTER APPLY (
SELECT TOP 1 Date FROM YourTable
WHERE ........
ORDER BY ..........
) N
WHERE T.Date IS NULL

How can I simplify this Query? I need to compare a temp variable value with a column value of multiple rows

I need to compare a temp variable value with a column value of multiple rows and perform Operations based on that.
| intSeqID | Value |
----------------------------
1 | 779.40
2 | 357.38
3 | NULL
4 | NULL
5 | NULL
6 | NULL
7 | NULL
8 | NULL
9 | NULL
10 | NULL
DECLARE #tmpRange NUMERIC(5,2)
SELECT #tmpRange = 636
Here I need to compare the value #tmpRange with Value from TABLE and perform operations based on it.
IF((#tmpRange < (select ISNULL(Value,0) from #tableA intSeqID=1)) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=2))) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=3))) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=9))) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=10)))
BEGIN
SELECT 'All'
END
ELSE IF ((#tmpRange < (select ISNULL(Value,0) from #tableA intSeqID=1)) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=2))) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=3))) AND
(#tmpRange< (select ISNULL(Value,0) from #tableA where intSeqID=9))))
BEGIN
SELECT '10'
END
END
How can i simplify this query to compare values. Or is there any other way to pick the values of multiple rows and compare the same with temp variable.
Here is one fairly simple way to do it:
Create and populate sample table (Please save us this step in your future questions)
DECLARE #tableA as table
(
intSeqID int identity(1,1),
Value numeric(5,2)
)
INSERT INTO #tableA VALUES
(779.40),
(357.38),
(256.32),
(NULL)
Declare and populate the variable:
DECLARE #tmpRange numeric(5, 2) = 636
The query:
;WITH CTE AS
(
SELECT TOP 1 intSeqId
FROM #TableA
WHERE #tmpRange < ISNUll(Value, 0)
ORDER BY Value
)
SELECT CASE WHEN intSeqId =
(
SELECT TOP 1 intSeqId
FROM #TableA
ORDER BY ISNUll(Value, 0)
) THEN 'All'
ELSE CAST(intSeqId as varchar(3))
END
FROM CTE
Result: 1.
See a live demo on rextester.
We can try to refactor your query using aggregations. We almost get away with no subquery except for just one, which is needed to distinguish the two conditions.
SELECT
CASE WHEN SUM(CASE WHEN #tmpRange < Value THEN 1 ELSE 0 END) = 4 AND
#tmpRange < (SELECT Value FROM #tableA WHEREA intSeqID = 10)
THEN 'All'
WHEN SUM(CASE WHEN #tmpRange < Value THEN 1 ELSE 0 END) = 4
THEN '10'
ELSE 'NONE' END AS label
FROM #tableA
WHERE intSeqID IN (1, 2, 3, 9)
You want to find the biggest record in Value, who is also smaller than your variable, correct?
--DECLARE #tableA TABLE (intSeqID tinyint, [Value] decimal(5,2))
--INSERT INTO #tableA SELECT 1, 400 UNION SELECT 2, 300 UNION SELECT 3, 200
--DECLARE #tmpRange decimal(5,2) = 250
SELECT TOP 1 *
FROM (
SELECT TOP 1 CONCAT('', intSeqID) AS intSeqID -- Can't UNION int to varchar.
FROM #tableA
WHERE ISNULL([Value], 0) < #tmpRange
ORDER BY intSeqID ASC
UNION
SELECT 'All' AS [?]
) AS T
ORDER BY intSeqID ASC

How do I replace strings of a table from another table column

How do I update/replace the value of the first table from the list of my second table in SQL. Sorry im not so good in using replace() of SQL especially replacing from values base from different table
First table.
ID | Value
======================
1 | Fruits[Apple]
2 | Fruits[Apple,Mango]
3 | Apple[Red,Green]
Second table
Search | Replace
=========================
Apple | Orange
Green | Yellow
You will need some kind of recursive replace.
something like a loop
declare #t1 table (ID int, Value varchar(max))
declare #t2 table (Search varchar(max), ReplaceWith varchar(max))
insert #t1 values (1, 'Fruits[Apple]'),(2, 'Fruits[Apple,Mango]'), (3, 'Apple[Red,Green]')
insert #t2 values ('Apple', 'Orange'),('Green', 'Yellow')
--loop nth times for rows that have more than one match
while exists(select top 1 * from #t1 inner join #t2 on charindex(Search, Value ) > 0)
begin
update #t1
set Value = replace(Value, Search, ReplaceWith)
from #t2
inner join #t1 on charindex(Search, Value ) > 0
end
select * from #t1
results
ID Value
----- -----------------------
1 Fruits[Orange]
2 Fruits[Orange,Mango]
3 Orange[Red,Yellow]
Alternatively, you could use recursive CTE
;with CTE(ID, Value, rec_count)
as (
select distinct ID, Value, 1 as rec_count from #t1 inner join #t2 on charindex(Search, Value ) > 0
union all
select ID, Value = replace(Value, Search, ReplaceWith), rec_count +1
from CTE
inner join #t2 on charindex(Search, Value ) > 0
)
update #t1
set Value= replaced.Value
from #t1 t
inner join
( select distinct ID, Value
from CTE c
where rec_count > 1
and rec_count = (select max(rec_count) from CTE where ID = c.ID) ) replaced on replaced.ID = t.ID
Simply use following UPDATE by cross-joined select statement and enjoy it! ;)
UPDATE tFirst
SET Value = REPLACE(tFirst.Value, tSecond.Search, tSecond.Replace)
FROM
[First] tFirst
CROSS JOIN [Second] tSecond

Find missing numbers in a column

I have this column in T-SQL:
1
2
3
7
10
have SQl a function for detect the missing numbers in the sequence 4,5,6 and 8,9
I have try
something like
if ( a-b >1 ) then we have a missing number
with coalesce but i dont understand .
Thanks by any orientation
You can try this:
DELCARE #a
SET #a = SELECT MIN(number) FROM table
WHILE (SELECT MAX(number) FROM table ) > #a
BEGIN
IF #a NOT IN ( SELECT number FROM table )
PRINT #a
SET #a=#a+1
END
The following query will identify where each sequence starts and the number that are missing:
select t.col + 1 as MissingStart, (nextval - col - 1) as MissingSequenceLength
from (select t.col,
(select min(t.col) from t t2 where t2.col > t.col) as nextval
from t
) t
where nextval - col > 1
This is using a correlated subquery to get the next value in the table.
I know this is a late answer, but here is a query that uses recursive table expressions to get the missing values between the minimum and maximum values in a table:
WITH CTE AS
(
--This is called once to get the minimum and maximum values
SELECT nMin = MIN(t.ID), MAX(t.ID) as 'nMax'
FROM Test t
UNION ALL
--This is called multiple times until the condition is met
SELECT nMin + 1, nMax
FROM CTE
WHERE nMin < nMax
)
--Retrieves all the missing values in the table.
SELECT c.nMin
FROM CTE c
WHERE NOT EXISTS
(
SELECT ID
FROM Test
WHERE c.nMin = ID
)
This was tested with the following schema:
CREATE TABLE Test
(
ID int NOT NULL
)
INSERT INTO Test
Values(1)
INSERT INTO Test
Values(2)
INSERT INTO Test
Values(3)
INSERT INTO Test
Values(7)
INSERT INTO Test
Values(10)

In a persisted field, how do you return the number of occurrences of a column within a different table's column

The following is required due to records being entered by 3rd parties in a web application.
Certain columns (such as Category) require validation including the one below. I have a table OtherTable with the allowed values.
I need to identify how many occurrences (ie: IF) there are of the current table's column's value in a different table's specified column. If there are no occurrences this results in a flagged error '1', if there are occurrences, then it results in no flagged error '0'.
If `Category` can be found in `OtherTable.ColumnA` then return 0 else 1
How can I do this please?
If Category can be found in OtherTable.ColumnA then return 0 else 1
You could use CASE with EXISTS
SELECT CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = Category
) THEN 0 ELSE 1 END AS ErrorCode
, Category
FROM [Table]
Edit: Here's a sql-fiddle: http://sqlfiddle.com/#!3/55a2e/1
Edit: I've only just noticed that you want to use a computed column. As i've read you can only use it with scalar values and not with sub-queries. But you can create a scalar valued function.
For example:
create table AllowedValues(ColumnA varchar(1));
insert into AllowedValues Values('A');
insert into AllowedValues Values('B');
insert into AllowedValues Values('C');
create table [Table](Category varchar(1));
insert into [Table] Values('A');
insert into [Table] Values('B');
insert into [Table] Values('C');
insert into [Table] Values('D');
insert into [Table] Values('E');
-- create a scalar valued function to return your error-code
CREATE FUNCTION udf_Category_ErrorCode
(
#category VARCHAR(1)
)
RETURNS INT
AS BEGIN
DECLARE #retValue INT
SELECT #retValue =
CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = #category
) THEN 0 ELSE 1 END
RETURN #retValue
END
GO
Now you can add the column as computed column which uses the function to calculate the value:
ALTER TABLE [Table] ADD ErrorCode AS ( dbo.udf_Category_ErrorCode(Category) )
GO
Here's the running SQL: http://sqlfiddle.com/#!3/fc49e/2
Note: as #Damien_The_Unbelieve has commented at the other answer, even if you persist the result with a UDF, the value won't be updated if the rows in OtherTable change. Just keep that in mind, so you need to update the table manually if desired with the help of the UDF.
select mt.*,IFNULL(cat_count.ct,0) as Occurrences from MainTable mt
left outer join (select ColumnA,count(*) as ct from OtherTable) cat_count
on mt.Category=cat_count.ColumnA
Result:
mt.col1 | mt.col2 | Category | Occurrences
### | ### | XXX | 3
### | ### | YYY | 0
### | ### | ZZZ | 1