interesting t-sql exercise - sql

I am trying to resolve t-sql exercise
I need to update first table with values from second by joining by id. If I can not join then use value from default ID (default iD is the Id that is null)
please run it to see it
declare #t as table (
[id] INT
,val int
)
insert into #t values (null, null)
insert into #t values (2, null)
insert into #t values (3, null)
insert into #t values (4, null)
declare #t2 as table (
[id] INT
,val int
)
insert into #t2 values (null, 11)
insert into #t2 values (2, 22)
insert into #t2 values (3, 33)
select * from #t
select * from #t2
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or t.id not in (select id from #t2))
and t2.id is null
)
select * from #t
here is result
--#t
id val
---------------
NULL NULL
2 NULL
3 NULL
4 NULL
--#t2
id val
---------------
NULL 11
2 22
3 33
--#t after update
id val
---------------
NULL 11
2 22
3 33
4 NULL
how to make val in last row equal 11?
4 11

This solution left joins to t2 twice and then does a coalesce.
The ON on the second join matches on records that failed on the join and then looks for the "Default" case.
UPDATE t
set t.val = COALESCE(t2.val,t3.val)
from #t as t
LEFT join #t2 as t2
on t.id = t2.id
LEFT JOIN #t2 t3
ON t2.id is null and t3.id is null
See it working here

try this for the update...
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or not exists (select * from #t2 where id = t.id))
and t2.id is null
)
Problem is with not in operator and null values. This would also work...
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or t.id not in (select id from #t2 where id is not null))
and t2.id is null
)

Here's a technique that may help.
Start with the kind of simple code you want to be writing:
MERGE INTO #t AS target
USING source
ON target.id = source.id
WHEN MATCHED THEN
UPDATE
SET val = source.val;
Then write a table expression (source) that satisfies the requirements.
Requirement 1: "joining by id"
-- simple existential quantification e.g.
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id FROM #t )
Requirement 2: "If I can not join then use value from default ID (default iD is the Id that is null)"
-- first find the id values in the target that do not exist in the source:
SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2
then cross join the result with the row from the source where Id is null:
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
At this point you will want to query some test data to ensure each query satisfies its respective requirement.
Union the above two results to form a single table expression:
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id
FROM #t )
UNION
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
Then plug the table expression into the MERGE boilerplate code:
WITH source
AS
(
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id
FROM #t )
UNION
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
)
MERGE INTO #t AS target
USING source
ON target.id = source.id
WHEN MATCHED THEN
UPDATE
SET val = source.val;

Related

conditional union multiple tables

This is based on union tables on value
declare #t1 table (val int,datatype1 int,datatype2 int ,datatype3 int)
declare #t2 table (val int,datatype1 int ,datatype2 int ,datatype3 int )
declare #t3 table (val int,datatype1 int ,datatype2 int ,datatype3 int )
insert into #t1 values (10,1,0,0),(31,1,0,0),(20,1,0,0)
insert into #t2 values (31,0,1,0),(4,0,1,0)
insert into #t3 values (31,0,0,1),(5,0,0,1);
Below is the changes in requirement(case):
1. need to union #t1,#t2 & #t3
(if same value exist #t1 & #t2 multiple rows and #t2 & t3 only 1 row)
2. if any duplicate value (there is no chance dup in same table)
i) suppose 31 in #t1 , 31 in #t2 then multiple rows are allowed
ii) suppose 31 in #t2 & #t3 then only one records i.e #t3 updated to #t2
iii) if 31 in #t1 ,#t2,#t3 only 2 records i.e #t1,#t2 records with #t3 details updated to #t2 records
Now i) & iii) are working fine
select val,
max(datatype1) datatype1,
max(datatype2)datatype2,
max(datatype3)datatype3
from (
select 't1' AS tab_name, * from #t1
union all
select 't2' AS tab_name,* from #t2
union all
select 't3' AS tab_name,* from #t3
) as data
group by val, CASE WHEN tab_name in ('t2') THEN 1 END
order by val;
But Current Result showing multiple records for case 2 also any help
Expected Result:
Your question is very hard to follow. I'm a bit lost on the conditions, but this rather simple query returns the results that you specify:
select val,
max(datatype1) as datatype1,
max(datatype2) as datatype2,
max(datatype3) as datatype3,
max(datatype4) as datatype4
from (select 't1' AS tab_name, t1.* from t1
union all
select 't2' AS tab_name, t2.* from t2
union all
select 't3' AS tab_name, t3.* from t3
) data
group by val;
Here is a db<>fiddle.
I wonder if the culmination of all your results is a relatively simple aggregation.

UNION CTE with ORDER BY on 3 Tables

This is not easy to explain, however, I do believe there is a much nicer way to do what I managed to do and hope to get some help.
I have 3 tables (T1, T2, and T3). I need to get the latest from T1 and T2, then with those results return the results from t3 or from the previous result if t3 is empty. So the LatestDate doesn't really matter if there is a record in t3. Also, if there is no data in t3 and the LatestDate is the same on t1 and t2 (rarely will happen, but want to plan accordingly), I want results from t1.
Here's a sample of what I got, mind you the actual query is has many more fields, but the concept is the same.
CREATE TABLE [dbo].[t1](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
CREATE TABLE [dbo].[t2](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
CREATE TABLE [dbo].[t3](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
INSERT t1 (Id, LatestDate) VALUES (1, CAST(N'2000-01-01T00:00:00.000' AS DateTime));
INSERT t1 (Id, LatestDate) VALUES (2, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t1 (Id, LatestDate) VALUES (3, CAST(N'2002-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (1, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (2, CAST(N'2002-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (4, CAST(N'2003-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (1, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (2, CAST(N'2000-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (5, CAST(N'2004-01-01T00:00:00.000' AS DateTime));
GO;
WITH CTE AS (
SELECT TOP 1 * FROM (
SELECT 2 AS Sort, * FROM t1 WHERE t1.id = #UserId
UNION
SELECT 3 AS Sort, * FROM t2 WHERE t2.id = #UserId
) AS t
ORDER BY
t.LatestDate DESC
)
SELECT TOP 1 * FROM (
SELECT TOP 1 * FROM CTE
UNION
SELECT 1 AS Sort, * FROM t3 WHERE t3.id = #UserId
) AS t
ORDER BY
t.Sort;
Expected results:
When #UserID = 1:
Sort Source Id LatestDate
1 t3 1 1/1/2001
When #UserID = 2:
Sort Source Id LatestDate
1 t3 2 1/1/2000
When #UserID = 3:
Sort Source Id LatestDate
2 t1 3 1/1/2002
When #UserID = 4:
Sort Source Id LatestDate
3 t2 4 1/1/2003
When #UserID = 5:
Sort Source Id LatestDate
1 t3 5 1/1/2004
Thanks!
If I understand you correctly you want something like:
DECLARE #UserID INT = 5;
WITH cte AS (
SELECT 1 AS src, 1 as [tbl], * FROM t1 WHERE id = #UserId
UNION ALL SELECT 1, 2, * FROM t2 WHERE id = #UserId
UNION ALL SELECT 3, 3, * FROM t3 WHERE id = #UserId
), cte2 AS (
SELECT * , ROW_NUMBER() OVER(ORDER BY src DESC, LatestDate DESC, tbl ASC) AS rn
FROM cte
)
SELECT Id, LatestDate
FROM cte2
WHERE rn = 1;
RextesterDemo
Using PARTITION BY you could move filtering #userId to final part:
DECLARE #UserID INT = 5;
WITH cte AS (
SELECT 1 AS src, 1 as [tbl], * FROM t1
UNION ALL SELECT 1, 2, * FROM t2
UNION ALL SELECT 3, 3, * FROM t3
), cte2 AS (
SELECT *
,ROW_NUMBER() OVER(PARTITION BY Id ORDER BY src DESC, LatestDate DESC, tbl ASC) AS rn
FROM cte
)
SELECT Id, LatestDate
FROM cte2
WHERE rn = 1
AND id = #UserID
Rextester Demo2
How's this?
This assumes you will take the t3 result if it exists
or the max result from T1 or T2 if not.
if exists(select * from t3 where ID= #ID)
(
select ID, Max(LatestDate), TN='T3'
from t3
where ID= #ID
)
else
(
select top 1 ID, Max(LatestDate) LD,TN
from (Select *,TN='t1' from T1
union all
Select *, TN='t2' from t2) as a
where id=#ID
group by ID,TN
order by LD desc
)

TSQL - The multi-part identifier bounding error

CREATE FUNCTION [dbo].[Test] (#ID INT, #VAL INT)
RETURNS #Return TABLE (ID INT, VAL INT)
AS
BEGIN
INSERT INTO #Return
SELECT #ID, #VAL
RETURN;
END
GO
DECLARE #T1 TABLE (ID INT IDENTITY(1,1), VAL INT)
DECLARE #T2 TABLE (ID INT, VAL INT)
INSERT INTO #T1
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
INSERT INTO #T2
SELECT 1,1
UNION
SELECT 2,4
UNION
SELECT 3,3
SELECT *
FROM #T1 T1
LEFT JOIN #T2 T2 ON T1.[ID] = T2.[ID]
LEFT JOIN [dbo].[Test] (1, COALESCE(T2.[VAL],T1.VAL)) T ON T1.ID = T.ID
GO
DROP FUNCTION [dbo].[Test]
GO
Goal:
To pass in T2.Val into the 2nd param of the fx if available, else pass in T1.Val. Changing the FX definition is not possible.
I can't seem to get this work. I tried ISNULL and that doesn't work either.
If you want to call a table valued function, use APPLY (OUTER APPLY in this case because you are using LEFT JOIN):
SELECT *
FROM #T1 T1 LEFT JOIN
#T2 T2
ON T1.[ID] = T2.[ID] OUTER APPLY
[dbo].[Test](1, COALESCE(T2.[VAL], T1.VAL) ) T;
If you want an additional condition, then use a WHERE clause:
SELECT *
FROM #T1 T1 LEFT JOIN
#T2 T2
ON T1.[ID] = T2.[ID] OUTER APPLY
[dbo].[Test](1, COALESCE(T2.[VAL], T1.VAL) ) T
WHERE t1.ID = T.ID;
That last condition seems strange, though. Why not just pass T1.ID into the function directly?

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

Merge multiple rows into single value using update Query

Here is the scenario. I have two tables. I want to merge multiple row value to single value using update query.
DECLARE #Table as Table
(
id int,
name varchar(10)
)
insert into #Table values(1,'a')
insert into #Table values(1,'b')
insert into #Table values(1,'c')
select * from #Table
DECLARE #Table2 as Table
(
id int,
name varchar(10)
)
insert into #Table2 values(1,'a')
update t2 set name = t1.name from #Table2 t2
inner join #Table t1 on t1.id=t2.id
select * from #Table2
I want output from #Table2 as by using update query
id name
----- --------
1 a,b,c
;WITH Table1 AS (
SELECT t.id
, STUFF((SELECT ',' + name
FROM #Table
WHERE id = t.id
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,1,'') AS name
FROM #Table t
GROUP BY t.id)
UPDATE t2
SET t2.name = t1.name
FROM #Table2 t2
INNER JOIN Table1 t1 ON t1.id=t2.id