how to update row = NULL when there is no match - sql

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:

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 to capture columns from joined tables using output clause?

I am updating a table called tableA by joining tableB and tableC at the same time I am capturing updated records into temp table using output clause from tableA . Now I want capture columns from table B for the updated data but output clause isn't allowing the same.
Eg:
Update SLC Set SLC.Datascrublevel = C.Datascrublevel
OUTPUT [Deleted].Systemcode,
[Deleted].Systemkey,
[Deleted].datascrublevel,
[Inserted].datascrublevel
INTO #TEMP1
FROM TABLEA SLC with(nolock)
INNER JOIN TABLEB SC ON SC.SystemCode = SLC.SystemCode
INNER JOIN TABLEC SL ON SL.SystemCode = SLC.SystemCode and SLC.SystemKey = SL.Systemkey
INNER JOIN #TEMP C ON SLC.Datascrublevel <> C.DataScrubLevel AND C.Systemcode = SLC.SystemCode and C.Systemkey = SLC.SystemKey
Now I want columns from tableB to capture into temp table using output clause. Please provide your advise if there are any alternative ways.
Just Like you have given it as [deleted].[Column Name] and [Inserted].[Column Name] add one more column as [SC].[Column Name]
Example :
IF OBJECT_ID('TempDb..#TABLEA') IS NOT NULL
DROP TABLE #TABLEA
IF OBJECT_ID('TempDb..#TABLEB') IS NOT NULL
DROP TABLE #TABLEB
IF OBJECT_ID('TempDb..#TABLEC') IS NOT NULL
DROP TABLE #TABLEC
IF OBJECT_ID('TempDb..#TABLED') IS NOT NULL
DROP TABLE #TABLED
CREATE TABLE #TABLEA
(
SeqNo INT IDENTITY(1,1),
MyDate DATE
)
CREATE TABLE #TABLEB
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20)
)
CREATE TABLE #TABLEC
(
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(20),
MyDate DATE
)
CREATE TABLE #TABLED
(
SeqNo INT,
MyDate DATE,
FullName VARCHAR(20)
)
INSERT INTO #TABLEA
(
MyDate
)
SELECT GETDATE()
UNION
SELECT GETDATE()+1
UNION
SELECT GETDATE()-1
INSERT INTO #TABLEB
(
FullName
)
VALUES('A'),('B'),('C')
INSERT INTO #TABLEC
(
FullName
)
VALUES('A'),('B'),('C')
UPDATE C
SET MyDate = A.MyDate
OUTPUT
deleted.SeqNo,
deleted.MyDate,
B.FullName
INTO #TABLED
FROM #TABLEC C
INNER JOIN #TABLEB B
ON C.FullName = B.FullName
INNER JOIN #TABLEA A
ON A.SeqNo = B.SeqNo
SELECT * FROM #TABLED

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 return one row with multiple column values?

I'm trying to create a report with one row per customer, however there are multiple rows per customer.
The current view is:
Customer Business Dept Type Status
-----------------------------------------------
019 Public null null null
019 null IT null null
019 null null Retail 0 --char(1)
My desired view is:
Customer Business Dept Type Status
-----------------------------------------------
019 Public IT Retail 0
I'm using SQL Server 2008 R2. There are more columns in my data set, but this is a sample. I'm unsure of how to achieve the results when my datatype is character and not INT based.
If this is a representative example, and each column will always have a single row with a value and the others will have nulls, you could use an aggregate max or min, which ignore nulls:
SELECT customer, MAX(business), MAX(dept), MAX(type), MAX(status)
FROM mytable
GROUP BY customer
try something this:
CREATE TABLE #tmp ([Customer] CHAR(3), [Business] VARCHAR(20), [Dept] VARCHAR(20), [Type] VARCHAR(20), [Status] CHAR(1))
INSERT INTO #tmp (Customer, Business) VALUES ( '019', 'Public')
INSERT INTO #tmp (Customer,Dept) VALUES ('019','IT')
INSERT INTO #tmp (Customer,[Type]) VALUES ('019','Retail')
INSERT INTO #tmp (Customer,[Status]) VALUES ('019','0')
SELECT * FROM #tmp AS t
SELECT t.Customer, t.Business, t2.Dept, t3.[Type], t4.[Status] FROM #tmp AS t
JOIN #tmp AS t2 ON t2.Customer = t.Customer
JOIN #tmp AS t3 ON t3.Customer = t.Customer
JOIN #tmp AS t4 ON t4.Customer = t.Customer
WHERE t.Business IS NOT NULL AND t2.Dept IS NOT NULL AND t3.[Type] IS NOT NULL AND t4.[Status] IS NOT NULL

Set column values in TableA to corresponding values from TableB if some value exist in TableC

I have a TableA that I just added two columns to, StartDate and StopDate. It also contain the column UserName.
I want to fill it with values from TableB that also has StartDate and StopDate. TableB has another column called UserId.
TableC has two columns, Id and Name.
This is what I want to do, explained with some kind of pseudocode:
for each row in TableB, where TableB.UserId exists in TableC.Id, take the corresponding row in TableA where TableA.UserName = TableC.Name and set the TableA.StartDate = TableB.StartDate & TableA.StopDate = TableB.StopDate.
I tried to create a join statement that would do it but the result was kind of embarrassing.
Help would be much appreciated.
Edit: What I have tried:
UPDATE TableA
SET STARTDATE = (
SELECT TableB.StartDate
FROM TableB
WHERE TableB.UserId = (??? what should I write here)?
And then same for StopDate
You can achieve this using the "update-from" syntax.
*I see you've already solved your question, but I'll post this as a possible alternative.
declare #TableA table (id int identity(1, 1), startDate datetime, stopDate datetime, userName varchar(20))
declare #TableB table (id int identity(1, 1), startDate datetime, stopDate datetime, userId int)
declare #TableC table (userId int, userName varchar(20))
insert into #TableA (userName)
select 'A' union all select 'B'
insert into #TableB (userId, startDate, stopDate)
select 1, '2015-01-01', '2015-01-31' union all select 2, '2015-12-01', '2015-12-31'
insert into #TableC
select 1, 'A' union all select 2, 'B'
update
A
set
A.startDate = B.startDate,
A.stopDate = B.stopDate
from
#TableA A
inner join #TableC C on C.userName = A.userName
inner join #TableB B on B.userId = C.userId
select
*
from
#TableA
I solved it, here is my solution
UPDATE TableA
SET StartDate = up.StartDate, EndDate = up.EndDate
FROM TableB up WHERE up.UserID = (SELECT Id from TableC u where u.Name = TableA.UserName)