I have two tables that have the same columns. If a change occurs it is being recorded in table2 and then I compare table1 to table2 and see if they are different. If they are different I conclude there was a change and I would like to display that in my resulting table.
For example:
SELECT t1.name, t1.age, t1.profession, column_changed, old_value, new_value
FROM table1 t1, table2 t2
WHERE t1.column1<>t2.column1
OR t1.column2<>t2.column2
OR t1.column3<>t2.column3
Of course this query isn't correct. I would like the column_changed, old_value, new_value display the relevant values.
Any ideas?
Does Age, name, profession form a primary key (or at least a unique key?):
If so, you could do something like:
SELECT
t1.name, t1.age, t1.profession,
t1.column1 as t1column1, t2.column1 as t2column1,
t1.column2 as t1column2, t2.column2 as t2column2,
FROM
table1 t1, table2 t2
WHERE
(t1.name = t2.name and t1.age = t2.age and t1.profession = t2.profession) and
(
t1.column1<>t2.column1
OR t1.column2<>t2.column2
OR t1.column3<>t2.column3)
Of course, that requires a unique key which is the same across both tables. Also, I clearly changed the results of the query to show all columns instead of just the one that changed. Identifying the one that changed like that in a single T-SQL Query is awkward (but possible) so my recommendation would be to return it like this and depending on your use case have the application/presentation layer handle finding which column changed or just scan it by eye.
If you really want to do it in T-SQL, you could do it with UNIONS, like:
SELECT
t1.name, t1.age, t1.profession,
'column1' as ColumnChanged,
t1.column1 as oldValue,
t2.column1 as newValue
FROM
table1 t1, table2 t2
WHERE
(t1.name = t2.name and t1.age = t2.age and t1.profession = t2.profession) and
t1.column1<>t2.column1
UNION ALL #better peformance than UNION, which would enforce uniqueness
SELECT
t1.name, t1.age, t1.profession,
'column2' as ColumnChanged,
t1.column2 as oldValue,
t2.column2 as newValue
FROM
table1 t1, table2 t2
WHERE
(t1.name = t2.name and t1.age = t2.age and t1.profession = t2.profession) and
t1.column2<>t2.column2
.......
Try this:
Select t1.name, t1.age, t1.profession,
case when t1.column1 <> t2.column1 then 'column1 changed'
when t1.column2 <> t2.column2 then 'column2 changed'
-- and so on...
end as [column_changed],
case when t1.column1 <> t2.column1 then t1.column1
when t1.column2 <> t2.column2 then t1.column2
-- and so on...
end as [old_value],
case when t1.column1 <> t2.column1 then t2.column1
when t1.column2 <> t2.column2 then t2.column2
-- and so on...
end as [new_value]
From table1 t1, table2 t2
Where t1.column1 <> t2.column1
Or t1.column2 <> t2.column2
Or t1.column3 <> t2.column3
After further brain storming this I have concluded that ganders solution works better with slight improvment. The improvement is a while loop and a count variable. We need to have that in case there are several columns changed at once and not just one. It will, however, result in outputting NULL's as well so you can just delete them. Here is the modified query:
WHILE #count<3
BEGIN
Select t1.name, t1.age, t1.profession,
case when t1.column1 <> t2.column1 and #count = 1 then 'column1 changed'
when t1.column2 <> t2.column2 #count = 2 then 'column2 changed'
-- and so on...
end as [column_changed],
case when t1.column1 <> t2.column1 #count = 1 then t1.column1
when t1.column2 <> t2.column2 #count = 2 then t1.column2
-- and so on...
end as [old_value],
case when t1.column1 <> t2.column1 #count = 1 then t2.column1
when t1.column2 <> t2.column2 #count = 2 then t2.column2
-- and so on...
end as [new_value]
From table1 t1, table2 t2
Where t1.column1 <> t2.column1 Or t1.column2 <> t2.column2 Or t1.column3 <> t2.column3
SET #counter = #counter + 1
END
Here's your solution rewritten as a single SELECT statement (or, if you like, #ganders's rewritten to support several changes in the same row):
SELECT
x.column_changed,
t1.column1 AS old_value,
t2.column1 AS new_value
FROM table1 t1
INNER JOIN table2 t2 ON t1.name = t2.name
INNER JOIN (
SELECT 'column1' UNION ALL
SELECT 'column2' UNION ALL
…
) x (column_changed) ON (
x.column_changed = 'column1' AND t1.column1 <> t2.column1 OR
x.column_changed = 'column2' AND t1.column2 <> t2.column2 OR
…
)
I added a join #T2 t2 on t2.name = t1.name to your query using the name column. Making the assumption that it can be used to link the records between the two tables.
select
t1.name, t1.age, t1.profession,
column_change =
case when t1.column1 <> t2.column1 then 'column1'
when t1.column2 <> t2.column2 then 'column2'
when t1.column3 <> t2.column3 then 'column3'
end,
old_value =
case when t1.column1 <> t2.column1 then t1.column1
when t1.column2 <> t2.column2 then t1.column2
when t1.column3 <> t2.column3 then t1.column3
end,
new_value =
case when t1.column1 <> t2.column1 then t2.column1
when t1.column2 <> t2.column2 then t2.column2
when t1.column3 <> t2.column3 then t2.column3
end
from #T1 t1
join #T2 t2 on t2.name = t1.name
where
(t1.column1 <> t2.column1
or t1.column2 <> t2.column2
or t1.column3 <> t2.column3)
Related
I want to do the following:
UPDATE table1 t1
SET column1 = 10 * t2.column1
FROM table2 t2
WHERE t1.column2 = 'yyy'
UPDATE table1 t1
SET column1 = 10 * t2.column2
FROM table2 t2
WHERE t1.column2 = 'zzz'
Is it possible to keep it in one semantic block?
You could use CASE WHEN .. and apply the update only at the rows with valid condition ( 'yyy' OR 'zzz' )
UPDATE table1 t1
SET column1 = CASE
WHEN t1.column2 = 'yyy' THEN column1 = 10 * t2.column1
WHEN t1.column2 = 'zzz' THEN column1 = 10 * t2.column2
end
FROM table2 t2
WHERE t1.column2 IN ( 'yyy','zzz')
UPDATE table1 t1
SET column1 = (
CASE
WHEN t1.column2 = 'yyy' THEN column1 = 10 * t2.column1
WHEN t1.column2 = 'zzz' THEN column1 = 10 * t2.column2
end)
FROM table2 t2
In SQL Server stored procedure I have this code:
UPDATE #tbinforesult
SET prifix = ''
FROM #tbmax t
INNER JOIN #tbinforesult i
ON i.prifix = t.prifix
AND i.PropertyID = t.PropertyID
AND i.GroupID = t.GroupID
AND i.NID <> t.id
What is the equivalent in pl-sql with pl-sql collections not global temporary table?
TSQL :
UPDATE
Table_A
SET
Table_A.col1 = Table_B.col1,
Table_A.col2 = Table_B.col2
FROM
Some_Table Table_A
INNER JOIN
Other_Table Table_B
ON
Table_A.id = Table_B.id
WHERE
Table_A.col3 = 'cool'
PLSQL:
UPDATE table1 t1
SET (name, desc) = (SELECT t2.name, t2.desc
FROM table2 t2
WHERE t1.id = t2.id)
WHERE EXISTS (
SELECT 1
FROM table2 t2
WHERE t1.id = t2.id )
OR:
UPDATE (SELECT t1.id,
t1.name name1,
t1.desc desc1,
t2.name name2,
t2.desc desc2
FROM table1 t1,
table2 t2
WHERE t1.id = t2.id)
SET name1 = name2,
desc1 = desc2
SOURCE :enter link description here
I'm working on a sql query that should 'coalesce' the records from 2 tables, i.e. if the record exists in table2, it should take that one, otherwise it should fall back to the values in table1.
In the example, table1 and table2 have just 2 fields (id an description), but obviously in reality there could be more.
Here's a small test case:
create table table1 (id int, description nvarchar(50))
create table table2 (id int, description nvarchar(50))
insert into table1 values (1, 'record 1')
insert into table1 values (2, 'record 2')
insert into table1 values (3, 'record 3')
insert into table2 values (1, 'record 1 modified')
insert into table2 values (2, null)
The result of the query should look like this:
1, "record 1 modified"
2, null
3, "record 3"
Here's what I came up with.
select
case when table2.id is not null then
table2.id else table1.id
end as Id,
case when table2.id is not null then
table2.description
else
table1.description
end as Description
-- etc for other fields
from table1
left join table2 on table1.id = table2.id
Is there a better way to achieve what I want? I don't think I can use coalesce since that would not select a null value from table2 if the corresponding value in table1 is not null.
How about:
SELECT t2.ID, t2.Description
FROM table2 t2
UNION ALL
SELECT t1.ID, t1.Description
FROM table1 t1
WHERE NOT EXISTS (SELECT *
FROM table2 t2
WHERE t2.ID = t1.ID)
The above query gets all the records from table 2 (including the case where description is NULL but the ID is populated), and only the records from table 1 where they don't exist in table 2.
Here's an alternative:
SELECT table2.*
FROM table1
RIGHT JOIN table2
ON table1.id = table2.id
UNION
SELECT table1.*
FROM table1
FULL OUTER join table2
ON table1.id = table2.id
WHERE table1.id NOT IN (SELECT id FROM table2)
--and table2.id not in (select id from table1)
You can add in that last line if you don't want ids that are only in table2. Otherwise I guess Stuart Ainsworth's solution is better (i.e. drop all the joins)
http://sqlfiddle.com/#!3/03bab/12/0
I have a (i think) complicated problem, and have no idea how to do that in SQL (the whole day). I have turned the logic around a couple of times, and always something is missing.
There is a join between 2 tables that hold different FK references to a 3rd table.
How to join those 2 tables, so i am sure that all FK combinations are presented, and all are unique?
I need to have the 2 FK columns in one, so i can later join to 3rd. nulls are possible. Group by not possible, since i need to know where the record is from (need Id_1 and Id_2 in the result)
here the sample code:
DECLARE #T1 TABLE (Id int, CommonId int, FK_Id_1 int)
DECLARE #T2 TABLE (Id int,CommonId int, FK_Id_2 int)
INSERT INTO #T1 VALUES (1,1,1)
INSERT INTO #T1 VALUES (2,1,2)
INSERT INTO #T1 VALUES (3,2,3)
INSERT INTO #T1 VALUES (4,3,NULL)
INSERT INTO #T1 VALUES (5,4,NULL)
INSERT INTO #T2 VALUES (11,1,1)
INSERT INTO #T2 VALUES (12,2,2)
INSERT INTO #T2 VALUES (13,2,3)
INSERT INTO #T2 VALUES (14,4,5)
SELECT t1.Id as Id_1,t2.Id as Id_2, t1.CommonId, t1.FK_Id_1, t2.FK_Id_2,
COUNT(t1.FK_Id_1) OVER (PARTITION BY t1.FK_Id_1) AS T1_RANK,
COUNT(t2.FK_Id_2) OVER (PARTITION BY t2.FK_Id_2)AS T2_RANK
FROM #T1 t1
FULL JOIN #T2 t2 on t1.CommonId = t2.CommonId
ORDER BY CommonId
This query is returning this:
Id_1 Id_2 CommonId FK_Id_1 FK_Id_2 T1_RANK T2_RANK
----------- ----------- ----------- ----------- ----------- ----------- -----------
1 11 1 1 1 1 2
2 11 1 2 1 1 2
3 12 2 3 2 2 1
3 13 2 3 3 2 1
4 NULL 3 NULL NULL 0 0
5 14 4 NULL 5 0 1
and i need somehow to make it look like this:
Id_1 Id_2 CommonId FK_Id
----------- ----------- ----------- -----------
1 11 1 1
2 11 1 2
3 12 2 2
3 13 2 3
4 NULL 3 NULL
5 14 4 5
I did something like SELECT COALESCE(FK_Id_1,FK_Id_2) AS FK_Id but this is always selecting T1 with priority. I am thinking of some way to switch the priority depending of the duplicated values.
i have a ugly solution that looks like this, but am looking for a better ideas.
;WITH tmp as (
SELECT t1.Id as Id_1,t2.Id as Id_2, t1.CommonId, t1.FK_Id_1, t2.FK_Id_2,
COUNT(t1.FK_Id_1) OVER (PARTITION BY t1.FK_Id_1) AS T1_RANK,
COUNT(t2.FK_Id_2) OVER (PARTITION BY t2.FK_Id_2)AS T2_RANK
FROM #T1 t1
FULL JOIN #T2 t2 on t1.CommonId = t2.CommonId)
SELECT Id_1, Id_2, CommonId,
CASE
WHEN T1_RANK > T2_RANK THEN COALESCE(FK_Id_2,FK_Id_1)
WHEN T2_RANK > T1_RANK THEN COALESCE(FK_Id_1,FK_Id_2)
END AS FK_Id
FROM tmp
ORDER BY CommonId
I don't know if i explained the whole situation correctly, i must join the tables, because i have other columns coming only from T1 and T2 ( can not UNION->DISTINCT - this will select also the NULLs)
just pick the CommonId, and then full join to both tables.
the query below matches 100% to your desired result.
;WITH cte AS (
SELECT CommonId FROM #T1
UNION SELECT CommonId FROM #T2
)
SELECT t1.Id AS Id_1, t2.Id AS Id_2, cte.CommonId, ISNULL(t2.FK_Id_2, t1.FK_Id_1) AS FK_Id
FROM cte
FULL OUTER JOIN #T1 t1 ON cte.CommonId = t1.CommonId
FULL OUTER JOIN #T2 t2 ON cte.CommonId = t2.CommonId
Beware that that the results of the FK_Id changes with the column order in the ISNULL in the predicate
ISNULL(t2.FK_Id_2, t1.FK_Id_1) is not the same as ISNULL(t1.FK_Id_1, t2.FK_Id_2)
In my opinion this alternative version fits better your request, since it's taking both options of FK.
;WITH cte AS (
SELECT CommonId FROM #T1
UNION SELECT CommonId FROM #T2
)
SELECT t1.Id AS Id_1, t2.Id AS Id_2, cte.CommonId, ISNULL(t2.FK_Id_2, t1.FK_Id_1) AS FK_Id--, cte.CommonId, *
FROM cte
FULL OUTER JOIN #T1 t1 ON cte.CommonId = t1.CommonId
FULL OUTER JOIN #T2 t2 ON cte.CommonId = t2.CommonId
UNION
SELECT t1.Id AS Id_1, t2.Id AS Id_2, cte.CommonId, ISNULL(t1.FK_Id_1, t2.FK_Id_2) AS FK_Id--, cte.CommonId, *
FROM cte
FULL OUTER JOIN #T1 t1 ON cte.CommonId = t1.CommonId
FULL OUTER JOIN #T2 t2 ON cte.CommonId = t2.CommonId
I have 2 tables.
Table 1:
Id Name
1 John
2 Mike
3 Sam
Table 2:
Name Data
John Data1
John Data1
John Data1
Mike Data2
Mike Data2
Sam Data3
If I write
select Table2.Name, Table2.Data
from Table1
inner join Table2 on Table1.Name= Table2.Name
I get all the duplicate data.
I would like to be able to retrieve something like:
John Data1
Mike Data2
Sam Data3
SELECT DISTINCT NAME
, DATA
FROM Table2
WHERE NAME IN (SELECT NAME
FROM Table1)
There are a few different options here...
There are two ways to do that.
You can use distinct clause:
select distinct t2.Name, t2.Data
from Table1 t1
inner join Table2 t2 on t1.Name= t2.Name
Here is link to MSDN.
You can use group by :
select t2.Name, t2.Data
from Table1 t1
inner join Table2 t2 on t1.Name= t2.Name
group by t2.Name, t2.Data
Here is link to MSDN.
I prefer second solution, because I always can add grouping functions.
Note:
In both queries I used aliases (t1,t2). It's more readable.
You can use CTE for this and apply a row_number()
;with cte as
(
select t1.name, t2.data,
row_number() over(partition by t1.id order by t1.id) rn
from table1 t1
inner join table2 t2
on t1.name = t2.name
)
select *
from cte
where rn = 1
Or a non-CTE version:
select *
from
(
select t1.name, t2.data,
row_number() over(partition by t1.id order by t1.id) rn
from table1 t1
inner join table2 t2
on t1.name = t2.name
) x
where rn = 1
see SQL Fiddle with Demo
Are you looking for something like:
select Table2.Name, Table2.Data, count(*) from Table1
inner join Table2 on Table1.Name= Table2.Name
group by Table2.Name, Table2.Data;