Compare two tables using field map - sql-server

I need to compare the values between tables in two SQL Server databases. The fieldnames in the tables in one database don't match the fieldnames in the tables in the second database. I have a link table that has the matching table names and the matching field names mapped.
Table1:
| Tab1_ID | Field1 | Field2 | Field3 |
|---------|--------|--------|--------|
| 1 | One | Two | Three |
| 2 | Two | Two | One |
| 3 | Three | Two | Two |
| 4 | Two | One | One |
Table2:
| Tab2_ID | Field_1 | Field_2 | Field_3 |
|---------|---------|---------|---------|
| 1 | One | Two | Three |
| 2 | Two | Five | One |
| 3 | Three | Two | Two |
| 4 | Two | One | Six |
Link Table:
| LinkTab_ID | Tab1 | Tab2 | Tab1Fld | Tab2Fld |
|------------|--------|--------|---------|--------------|
| 100 | Table1 | Table2 | Field1 | Field_1 |
| 105 | Table1 | Table2 | Field2 | Field_2 |
| 110 | Table1 | Table2 | Field3 | Field_3 |
| 124 | Table1 | Table4 | Field1 | Fieldname_01 |
| 166 | Table3 | Table5 | F3 | FN_3 |
Is it possible to use the Link Table to somehow specify the field names to compare between the two tables?
typically I'd do something like
SELECT
*
FROM
Table1 INNER JOIN Table2 ON Tab1_ID = Tab2_ID
WHERE
Table1.Field1 != Table2.Field_1
OR Table1.Field2 != Table2.Field_2
However I have many tables and many fields and the fieldnames change (i.e. new fields). My one constant is that the two are mapped in the link table.
The tables are one-to-one and fields are one-to-one.

This is only an approach you will need to expand it to suit. In particular you need a method for handling the join predicate(s).
also see this SQL Fiddle
CREATE TABLE LinkTable
([LinkTab_ID] int, [Tab1] varchar(6), [Tab2] varchar(6), [Tab1Fld] varchar(6), [Tab2Fld] varchar(12))
;
INSERT INTO LinkTable
([LinkTab_ID], [Tab1], [Tab2], [Tab1Fld], [Tab2Fld])
VALUES
(100, 'Table1', 'Table2', 'Field1', 'Field_1'),
(105, 'Table1', 'Table2', 'Field2', 'Field_2'),
(110, 'Table1', 'Table2', 'Field3', 'Field_3'),
(124, 'Table1', 'Table4', 'Field1', 'Fieldname_01'),
(166, 'Table3', 'Table5', 'F3', 'FN_3')
;
CREATE TABLE Table1
([Tab1_ID] int, [Field1] varchar(5), [Field2] varchar(3), [Field3] varchar(5))
;
INSERT INTO Table1
([Tab1_ID], [Field1], [Field2], [Field3])
VALUES
(1, 'One', 'Two', 'Three'),
(2, 'Two', 'Two', 'One'),
(3, 'Three', 'Two', 'Two'),
(4, 'Two', 'One', 'One')
;
CREATE TABLE Table2
([Tab2_ID] int, [Field_1] varchar(9), [Field_2] varchar(9), [Field_3] varchar(9))
;
INSERT INTO Table2
([Tab2_ID], [Field_1], [Field_2], [Field_3])
VALUES
('1', 'One', 'Two', 'Three'),
('2', 'Two', 'Five', 'One'),
('3', 'Three', 'Two', 'Two'),
('4', 'Two', 'One', 'Six')
;
Query 1:
DECLARE #t1 AS NVARCHAR(30) = 'Table1'
DECLARE #t2 AS NVARCHAR(30) = 'Table2'
DECLARE #filter AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
SET #filter = STUFF((SELECT ' OR ' + concat(Tab1, '.', Tab1Fld, ' <> ', Tab2, '.', Tab2Fld)
FROM LinkTable
WHERE Tab1 = #t1 AND Tab2 = #t2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,4,'')
SET #query = 'SELECT * FROM '
+ #t1
+ ' INNER JOIN '
+ #t2
+ ' ON '
+ #t1
+ '.Tab1_ID = '
+ #t2
+ '.Tab2_ID'
+ ' WHERE '+ #filter
select #query
--execute(#query)
Results:
| SELECT * FROM Table1 INNER JOIN Table2 ON Table1.Tab1_ID = Table2.Tab2_ID WHERE Table1.Field1 <> Table2.Field_1 OR Table1.Field2 <> Table2.Field_2 OR Table1.Field3 <> Table2.Field_3 |

Related

Merge multiple results to one Column

I would like to create a select statement to merge multiple results into one rot
I was able to realized what I want via PHP but i think it should be possible to work directly with Sql.
Notes
| ID | Name |
|----|------|
| 2 | Test |
| 3 | Test |
EditorAssignment
| UserID | NoteID |
|--------|--------|
| 1 | 2 |
| 2 | 2 |
UserList
| ID | username |
|----|----------|
| 1 | testuser |
| 2 | bUser |
| 3 | cUser |
I would like to have the following Result:
| NoteID | User |
|--------|-----------------|
| 2 | testuser, bUser |
| 3 | cUser |
How can i create a select like that in SQL2012?
Before SQL Server 2017 (which has STRING_AGG aggregate function for this), you need to use the famous FOR XML PATH correlated subquery:
SetUp:
DECLARE #Notes TABLE (ID INT, Name VARCHAR(10))
INSERT INTO #Notes (ID, Name)
VALUES (2, 'Test'), (3, 'Test')
DECLARE #EditorAssignment TABLE (UserID INT, NoteID INT)
INSERT INTO #EditorAssignment (UserID, NoteID)
VALUES (1, 2), (2, 2),
(3, 3) -- Added missing row here
DECLARE #UserList TABLE (ID INT, username VARCHAR(100))
INSERT INTO #UserList (ID, username)
VALUES (1, 'testuser'), (2, 'bUser'), (3, 'cUser')
Query:
SELECT
NoteID = N.ID,
[User] = STUFF(
(
SELECT
', ' + U.username
FROM
#EditorAssignment AS E
INNER JOIN #UserList AS U ON E.UserID = U.ID
WHERE
N.ID = E.NoteID -- Link the outmost Notes' note with the inner EditorAssignment (correlated subquery)
FOR XML
PATH ('') -- FOR XML PATH('') makes the SELECT return a string value, not a result set
),
1,
2,
'')
FROM
#Notes AS N
Results:
NoteID User
2 testuser, bUser
3 cUser
The subquery generates a string with each username for the related assignment for each note.
The STUFF function is used just to replace the first ', ', since the string is build with a leading comma and a space. This is why the parameters of the STUFF are 1 (first position of the string), 2 (amount of places to remove, the comma and the space), and '' (replacement for those characters).
I'm guessing that you have a row missing for the EditorAssignment table that holds the values 3, 3 on your example.

Track the changes of a few columns in an existing table leveraging primary keys?

I'm currently trying to track the changes of a few columns (let's call them col1 & col2) in a SQL Server table. The table is not being "updated/inserted/deleted" over time; new records are just being added to it (please see below 10/01 vs 11/01).
My end-goal would be to run a SQL query or stored procedure that would highlight the changes overtime using primary keys following the framework:
PrimaryKey | ColumnName | BeforeValue | AfterValue | Date
e.g.
Original table:
+-------+--------+--------+--------+
| PK1 | Col1 | Col2 | Date |
+-------+--------+--------+--------+
| 1 | a | e | 10/01 |
| 1 | b | e | 11/01 |
| 2 | c | e | 10/01 |
| 2 | d | f | 11/01 |
+-------+--------+--------+--------+
Output:
+--------------+--------------+---------------+--------------+--------+
| PrimaryKey | ColumnName | BeforeValue | AfterValue | Date |
+--------------+--------------+---------------+--------------+--------+
| 1 | Col1 | a | b | 11/01 |
| 2 | Col1 | c | d | 11/01 |
| 2 | Col2 | e | f | 11/01 |
+--------------+--------------+---------------+--------------+--------+
Any help appreciated.
Here is some code which is a bit clunky, but seems to work, Basically for each row I try and find an earlier row with a different value. This is done twice, once for Col1 and once for Col2.
To make it work I had to add a unique PK field, which I don't know whether you have or not, you can easily add as an identify field, either to your real table, or to the table used for the calculations.
declare #TestTable table (PK int, PK1 int, Col1 varchar(1), Col2 varchar(1), [Date] date)
insert into #TestTable (PK, PK1, Col1, Col2, [Date])
select 1, 1, 'a', 'e', '10 Jan 2018'
union all select 2, 1, 'b', 'e', '11 Jan 2018'
union all select 3, 2, 'c', 'e', '10 Jan 2018'
union all select 4, 2, 'd', 'f', '11 Jan 2018'
select T1.[Date], T1.PK1, 'Col1', T2.Col1, T1.Col1
from #TestTable T1
inner join #TestTable T2 on T2.PK = (
select top 1 PK
from #TestTable T21
where T21.PK1 = T1.PK1 and T21.Col1 != T1.Col1 and T21.[Date] < T1.[Date]
order by T21.[Date] desc
)
union all
select T1.[Date], T1.PK1, 'Col2', T3.Col2, T1.Col2
from #TestTable T1
inner join #TestTable T3 on T3.PK = (
select top 1 PK
from #TestTable T31
where T31.PK1 = T1.PK1 and T31.Col2 != T1.Col2 and T31.[Date] < T1.[Date]
order by T31.[Date] desc
)
order by [Date], PK1

SQL Server table combination in a single query

In SQL Server, is it possible to, in a single query regardless of how complex, combine two table in the manner below?
Using a 'full join' I'll have to choose the .name from both tables resulting in duplicate columns, and using a 'union all' I'll get duplicates rows.
Table 1:
+---------+--------+
| name | value1 |
+---------+--------+
| Abel | a |
| Baker | b |
+---------+--------+
Table 2:
+---------+--------+
| name | value2 |
+---------+--------+
| Baker | x |
| Charlie | y |
+---------+--------+
Query output:
+---------+--------+--------+
| name | value1 | value2 |
+---------+--------+--------+
| Abel | a | NULL |
| Baker | b | x |
| Charlie | NULL | y |
+---------+--------+--------+
Using FULL OUTER JOIN you can achieve your expectation.
CREATE TABLE #table1 (name varchar (200), value1 VARCHAR(200))
INSERT INTO #table1
SELECT 'Abel', 'a' UNION
SELECT 'Baker', 'b'
GO
CREATE TABLE #table2 (name varchar (200), value2 VARCHAR(200))
INSERT INTO #table2
SELECT 'Baker', 'x' UNION
SELECT 'Charlie', 'y'
GO
SELECT ISNULL(t1.name, t2.name) AS name, t1.value1, t2.value2
FROM #table1 t1
FULL OUTER JOIN #table2 t2 ON t2.name = t1.name
DROP TABLE #table1
DROP TABLE #table2

Split column with delimited values for an inner join

I need to perform an inner join to a column containing delimited values like:
123;124;125;12;3433;35343;
Now what I am currently doing is this:
ALTER procedure [dbo].[GetFruitDetails]
(
#CrateID int
)
AS
SELECT Fruits.*, Fruits_Crates.CrateID
FROM Fruits_Crates INNER JOIN Fruits
ON Fruits_Crates.FruitID = Fruits.ID
WHERE Fruits_Crates.CrateID = #CrateID
Now the issue is I am saving the data this way:
FruitCrateID FruitID
1 1;
2 1;2;3;4
3 3;
How can I inner join FruitsIDs to the fruit table to get fruit details as well?
Using the method posted in this answer, you can convert the delimited string into rows of a temp table and then join to that:
SQL Fiddle
Schema Setup:
CREATE TABLE Fruits_Crates
([FruitCrateID] int, [FruitID] varchar(10))
;
INSERT INTO Fruits_Crates
([FruitCrateID], [FruitID])
VALUES
(1, '1;'),
(2, '1;2;3;4;'),
(3, '3;')
;
CREATE TABLE Fruits
([FruitID] int, [FruitName] varchar(10))
;
INSERT INTO Fruits
([FruitID], [FruitName])
VALUES
(1, 'Apple'),
(2, 'Banana'),
(3, 'Orange'),
(4, 'Pear')
;
Insert to temp table:
SELECT A.[FruitCrateID],
Split.a.value('.', 'VARCHAR(100)') AS FruitId
INTO #fruits
FROM (SELECT [FruitCrateID],
CAST ('<M>' + REPLACE([FruitID], ';', '</M><M>') + '</M>' AS XML) AS String
FROM Fruits_Crates) AS A CROSS APPLY String.nodes ('/M') AS Split(a)
Join temp table to lookup:
SELECT t1.*, t2.FruitName
FROM #Fruits t1
INNER JOIN Fruits t2 on t1.FruitId = t2.FruitId
Results:
| FRUITCRATEID | FRUITID | FRUITNAME |
|--------------|---------|-----------|
| 1 | 1 | Apple |
| 2 | 1 | Apple |
| 2 | 2 | Banana |
| 2 | 3 | Orange |
| 2 | 4 | Pear |
| 3 | 3 | Orange |

Need help pivoting some data

I'm hoping someone can help me. I'm trying to pivot some data on SQL Server 2005 and can't quite get the results I'm looking for.
This is my current table schema:
| ProductCode | AttributeName | AttributeValue |
| 1 | AttributeA | 10 |
| 1 | AttributeB | 20 |
| 2 | AttributeA | 30 |
| 2 | AttributeB | 40 |
| 3 | AttributeA | 50 |
This is the results I'm trying to achieve:
| ProductCode | AttributeA | AttributeB |
| 1 | 10 | 20 |
| 2 | 30 | 40 |
| 3 | 50 | NULL |
I know that I can achieve this result with the following SQL:
SELECT DISTINCT ProductCode,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeA' AND ProductCode=a.ProductCode) AttributeA,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeB' AND ProductCode=a.ProductCode) AttributeB,
FROM attributes a
Although that SQL does produce the result I'm looking for, it's obviously not dynamic (in reality, I not only have more Attribute Types, but different products have different sets of attributes) and it also scans the table 3 times. It's also a maintenance nightmare.
I tried using the PIVOT functionality of SQL Server, but with no luck.
Can anyone help?
create table #attributes (ProductCode int,
AttributeName varchar(20),
AttributeValue int)
insert into #attributes values (1, 'AttributeA', 10)
insert into #attributes values (1, 'AttributeB', 20)
insert into #attributes values (2, 'AttributeA', 30)
insert into #attributes values (2, 'AttributeB', 40)
insert into #attributes values (3, 'AttributeA', 50)
declare #attributes_columns nvarchar(max)
set #attributes_columns
= (
select ', [' + AttributeName + ']'
from
(
select distinct AttributeName as AttributeName
from #attributes
) t
order by t.AttributeName
for xml path('')
)
set #attributes_columns = stuff(#attributes_columns,1,2,'')
declare #sql nvarchar(max)
set #sql = N'
select ProductCode, <attributes_columns>
from
(select ProductCode, AttributeName, AttributeValue
from #attributes )p
pivot
(
sum(AttributeValue) for AttributeName in (<attributes_columns>)
) as pvt
'
set #sql = replace(#sql, '<attributes_columns>', #attributes_columns)
print #sql
exec sp_executesql #sql
drop table #attributes

Resources