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

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

Related

how to update row = NULL when there is no match

i have 2 Tables like this:
CREATE TABLE #targetTable(id int,date datetime,name varchar(50));
CREATE TABLE #sourceTable(id int,date datetime,name varchar(50));
INSERT INTO #targetTable values(1,'1905-07-08 00:00:00.000','John');
INSERT INTO #targetTable values(2,'1905-07-08 00:00:00.000','Albrt');
INSERT INTO #targetTable values(3,'1905-07-08 00:00:00.000','Roy');
INSERT INTO #sourceTable values(1,'1905-07-09 00:00:00.000','jame');
i want to update the Target Table, when not match then update Name Column of Target with NULL. i would like to have this Result:
id date name
1 1905-07-09 00:00:00.000 jame
2 1905-07-09 00:00:00.000 null
3 1905-07-09 00:00:00.000 null
my test Query doesn't work: Error: UPDATE IS NOT ALLOWED IN THE WHEN NOT MATCHED. is there anyway to edit the query to get the Results?
merge into #targetTable a
using #sourceTable b on a.id=b.id
when matched and b.date > a.date then
update
set a.name=b.name,a.date=b.date
when not matched by Target then
update a.date=b.date and a.name = null
it shows me Error. Can you please help me how to get the Result?
No offense but your query has whole a lot of syntax issues.
Secondly, a merge statement cannot update values in the target table when not matched. You should try inserting instead.
Here is a example:
MERGE INTO #targetTable a
USING #sourceTable b
ON a.id=b.id
WHEN MATCHED THEN
UPDATE SET
a.name=b.name,
a.date=b.date
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
id,
date,
name
)
VALUES
(
b.id,
b.date,
null as name
)
After reading the question and all the comments what you really want is
UPDATE the row based upon the id with both the date and name but set the name to NULL when there is no match and set the date to the max date at the same time with that condition.
UPDATE with new values (name,date) when there is a match on id
I used a table variable for simplicity here but the concept is the
same.
Given the table and data this produces:
DECLARE #targetTable TABLE (id int,date datetime,name varchar(50));
DECLARE #sourceTable TABLE (id int,date datetime,name varchar(50));
INSERT INTO #targetTable
VALUES
(1,'1905-07-08 00:00:00.000','John'),
(2,'1905-07-08 00:00:00.000','Albrt'),
(3,'1905-07-08 00:00:00.000','Roy');
INSERT INTO #sourceTable values(1,'1905-07-09 00:00:00.000','jame');
SELECT id,date,name FROM #targetTable;
SELECT id,date,name FROM #sourceTable;
id date name
1 1905-07-08 00:00:00.000 John
2 1905-07-08 00:00:00.000 Albrt
3 1905-07-08 00:00:00.000 Roy
id date name
1 1905-07-09 00:00:00.000 jame
What you desire is basically the same as this select statement:
SELECT
t.id,
CASE
WHEN s.id IS NOT NULL THEN s.[date]
ELSE (SELECT MAX([date]) FROM #sourceTable )
END AS [date],
CASE
WHEN s.id IS NULL THEN NULL
ELSE s.name
END AS [name]
FROM #targetTable AS t
LEFT OUTER JOIN #sourceTable AS s
ON t.id = s.id;
SO to do that, we can incorporate that into an update:
UPDATE #targetTable
SET [date] = CASE
WHEN s.id IS NOT NULL THEN s.[date]
ELSE (SELECT MAX([date]) FROM #sourceTable )
END,
[name] = CASE
WHEN s.id IS NULL THEN NULL
ELSE s.name
END
FROM #targetTable AS t
LEFT OUTER JOIN #sourceTable AS s
ON t.id = s.id;
Final output
SELECT id,date,name FROM #targetTable;
id date name
1 1905-07-09 00:00:00.000 jame
2 1905-07-09 00:00:00.000 NULL
3 1905-07-09 00:00:00.000 NULL
I would use APPLY :
UPDATE t
SET t.name = s.name,
t.date = s.date
FROM #targetTable t OUTER APPLY
( SELECT TOP (1) s.*
FROM #sourceTable s
WHERE s.id = t.id
) s;
Your merge has Source value also :
merge #targetTable a
using #sourceTable b
on a.id=b.id
when matched
then update
set a.name = b.name, a.date= b.date
when not matched by SOURCE
then update
set a.name = null;
You need WHEN NOT MATCHED BY SOURCE, so something like:
WHEN NOT MATCHED BY SOURCE
THEN UPDATE
SET a.name = NULL
So if I get your problem, you simply want to update the name in table #targetTable if there is no matching row in #sourceTable.
You don't need a MERGE statement to accomplish this.
UPDATE #targetTable
SET
[name] = COALESCE([source].name, NULL)
, date = [source_date].maxDate
FROM
#targetTable AS [target]
LEFT JOIN #sourceTable AS [source] ON [target].id = [source].id
CROSS JOIN (SELECT max(date) AS maxDate FROM #sourcetable) AS [source_date]
WHERE
[source].id IS NULL
This yields the following output:

SQL Server hierarchy referencing and cross data referencing

This might be a stupid question, but I am not a DBA and kind of stuck with this issue. I have an application that trickles down all effects (asdf) under an applied ID (IDParent).
The data tables are setup like this:
Data Tables
3rd Data Table
I want to write a query that when using IDChild it will reference that entry's IDParent to get the parent ID while referencing it as an IDChild. For example for the data entry starting at 116 I want to use the parent ID (124) and get 321 in T1. I want to use this to get the RandoName associated with RandoID for all of the entries that has a parent ID of 321.
Right now I am using a script something like:
Select t.[NAME]
From T2 tv
Inner join T3 t on t.RandoID = tv.RandoId
Where
tv.IDChild = T1.IDChild OR tv.IDChild = T1.IDParent
but I'm not sure how to get the whole applied hierarchy.
This would yield something like this:
Resulting Query
PS. I can not change the tables/db schema. But maybe I can add one to do all the referencing? Please tell me what you think.
EDIT I'm sorry I forgot about this other stupid table that RandoID uses which contains the name of the RandoID. I am trying to get the name of RandoID
I think a loop could help you.
Try this:
CREATE TABLE #t1 (IDChild Int, IDParent Int);
CREATE TABLE #t2 (RandoID NVARCHAR(10) , IDChild Int);
CREATE TABLE #RandoName (RandoID NVARCHAR(10), RandoName VARCHAR(50));
INSERT INTO #t1 VALUES (321, NULL), (123,321),(124,123),(116,124)
INSERT INTO #t2 VALUES ('asdf', 123)
INSERT INTO #RandoName VALUES ('asdf', 'something')
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [RowNum], a.IDChild a, a.IDParent b, b.IDChild c INTO #t3 FROM #t1 a
LEFT OUTER JOIN #t1 b ON b.IDParent = a.IDChild
DECLARE #rownum INT;
DECLARE cbcursor CURSOR for Select RowNum FROM #t3;
OPEN cbcursor;
Fetch Next from cbcursor into #rownum
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #t3
SET c = (SELECT b from #t3 where RowNum = #rownum-1)
WHERE RowNum = #rownum
Fetch Next from cbcursor into #rownum;
END;
Close cbcursor;
Deallocate cbcursor;
SELECT a,b,t2.RandoID, r.RandoName FROM #t3
LEFT OUTER JOIN #t2 t2 on t2.IDChild = #t3.c OR t2.IDChild = #t3.b OR t2.IDChild = #t3.a
LEFT OUTER JOIN #RandoName r on t2.RandoID = r.RandoID
This is what I get:
If you have any changes in your tables, like more records for T2, this script should be modified.
Using recursion:
declare #t table (IDc int , Idp int)
insert into #t
values
(321,null)
,(123,321)
,(124,123)
,(116,124)
declare #t2 table (RandoID varchar(10), IDChild int)
insert into #t2
values('asdf',123)
;with cte as
(
select anchor = IDChild
,ParentOrSelf = IDc
,RandoID
,RandomName
from #t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) crossed
where IDc=#anchor
union all
select t2.IDChild
,IDc
, t2.RandoID,RandomName
from #t t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) t2
join cte on cte.ParentOrSelf = t.Idp
)
select IDc
, cte.RandoID,cte.RandomName
from #t t
left join cte on t.IDc = cte.ParentOrSelf
Results:
IDc RandoID
321 NULL
123 asdf
124 asdf
116 asdf

SQL Server: Delete from table based on value of another table

I want to delete from t2 if same value of itemid,storeid,MSRTime does not exist on t1 and Same value of itemid,storeid,MSRTime exist on t3 and status is D. In below example i should be able to delete second row on t2 but not 1st row.
Table 1: t1
itemid |storeid|MSRTime
x y z
Table 2: t2
itemid |storeid|MSRTime
x y z
a b c
Table 3: t3
itemid |storeid|MSRTime|status
x y z D
a b c D
I tried doing this using join but i could not reach the desired result. Please help.
Thank You.
You can write the query almost exactly as you've described it:
declare #t1 table(itemid varchar(7),storeid varchar(9),MSRTime varchar(3))
insert into #t1(itemid,storeid,MSRTime) values
('x','y','z')
declare #t2 table(itemid varchar(7),storeid varchar(9),MSRTime varchar(3))
insert into #t2(itemid,storeid,MSRTime) values
('x','y','z'),
('a','b','c')
declare #t3 table(itemid varchar(7),storeid varchar(9),MSRTime varchar(3),status varchar(4))
insert into #t3(itemid,storeid,MSRTime,status) values
('x','y','z','D'),
('a','b','c','D')
delete from t2
from #t2 t2
inner join
#t3 t3
on
t2.itemid = t3.itemid and
t2.storeid = t3.storeid and
t2.MSRTime = t3.MSRTime and
t3.status = 'D'
where
not exists (
select *
from #t1 t1
where t1.itemid = t2.itemid and
t1.storeid = t2.storeid and
t1.MSRTime = t2.MSRTime
)
select * from #t2
Result:
itemid storeid MSRTime
------- --------- -------
x y z
Should be something like this
-- delete t2
select *
from table2 t2
JOIN table3 t3 on t2.itemid = t3.itemid and
t2.storeid = t3.storeid and
t2.MSRTime = t3.MSRTime
LEFT JOIN table1 t1 on t2.itemid = t1.itemid and
t2.storeid = t1.storeid and
t2.MSRTime = t1.MSRTime
where t1.itemID IS NULL
Run the select first, if it gives you back right row, just un-comment delete and you are good to go
I have created the whole script for your reference. Please use the last DELETE query for your scenario. That will do the trick.
CREATE TABLE #T1
(itemid VARCHAR(10)
,storeid VARCHAR(10)
,MSRTime VARCHAR(10))
INSERT INTO #T1 VALUES ('x','y','z')
SELECT * FROM #T1
GO
CREATE TABLE #T2
(itemid VARCHAR(10)
,storeid VARCHAR(10)
,MSRTime VARCHAR(10))
INSERT INTO #T2 VALUES ('x','y','z'),('a','b','c')
SELECT * FROM #T2
GO
CREATE TABLE #T3
(itemid VARCHAR(10)
,storeid VARCHAR(10)
,MSRTime VARCHAR(10)
,status VARCHAR(10))
INSERT INTO #T3 VALUES ('x','y','z','D'),('a','b','c','D')
SELECT * FROM #T3
GO
DELETE M
FROM #T2 AS M INNER JOIN
(SELECT itemid,storeid,MSRTime FROM
(SELECT itemid,storeid,MSRTime FROM #T3 WHERE status='D') T1
INTERSECT
(SELECT itemid,storeid,MSRTime FROM
(SELECT * FROM #T2
EXCEPT
SELECT * FROM #T1) T2)) X
ON X.itemid = M.itemid AND X.storeid = M.storeid AND X.MSRTime = M.MSRTime
GO
Not sure if this matches your environment, but programmatically it may be beneficial to limit the results you are comparing with the joins to only those that have a value of D in status. I would also try making a compound key using Coalese so that you are not having to match on three separate joins.
For example -
itemid |storeid|MSRTime|Key
x y z xyz
a b c abc

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

How to collect pairs of data based on timestamp

I have this table:
What query should I write in order to get all the pairs of inputs that happen in a timeframe of up to 1 minute.
The end results that I need to receive from the above table should be:
I did it in t-sql but I think its not so hard to translate in mysql.
Assuming that timestamp column is of datetime type then this query does what you are asking for (I tested it in t-sql so I created a table variable you have to use your table):
declare #table table (userid int, input varchar(50),timestmp datetime)
insert into #table values(1,'hy','1/19/2017 12:00')
insert into #table values(2,'by','1/19/2017 12:00')
insert into #table values(1,'hy2','1/19/2017 12:01')
insert into #table values(2,'by2','1/19/2017 12:01')
insert into #table values(3,'why','1/19/2017 12:01')
select t1.userid,t1.input,t2.input from #table t1 inner join #table t2 on t1.userid=t2.userid and t1.timestmp=DATEADD(mi,-1,t2.timestmp)
Try this query, it is giving expected output.
SELECT t1.userid,t1.input,t2.input
FROM test_table t1
INNER JOIN test_table t2
ON TIMESTAMPDIFF(MINUTE,t1.timestmp,t2.timestmp) <= 1
AND t1.userid = t2.userid
Note : Replace your table_name with test_table
Ask in case of any doubt.