Problems synchronizing two tables in the source to one table in target node - symmetricds

I have this scenario:
Source node:
Schema1:
Table1: id, field1, field2
Table2: id, table1_id, table3_id
Table3: id
Table4: id, table1_id
Schema2:
Table5: id, table3_id, table6_id, field3, field4
Table6: id
Target Node:
Schema1:
Table7: id, field1, field2, field3, field4.
To solve this, I have created one router of "default" type, triggers for each table and the corresponding rows in trigger_router table. After that, I have created one row in transform_table to manage transformations from Table1 in source node, to Table 7 in target node. Here is my problem: At first, I tried to create one row in transform_table for getting data from Table6 to Table7, but I can't use any primary Key to link Table1 and Table6 in a direct way in the source. Now I'm trying to use lookup transformation to get field3 and field4.In order to achieve that, I have created a row in transform_column table like this:
TARGET_COLUMN_NAME: field3
SOURCE_COLUMN_NAME: null
PK: 0
TRANSFORM_TYPE:lookup
TRANSFORM_EXPRESSION:
SELECT field3
FROM schema1.table1 s1t1
INNER JOIN schema1.table2 s1t2 ON s1t2.table1_id = s1t1.id
LEFT JOIN schema1.table3 s1t3 ON s1t3.id = s1t2.table3_id
LEFT JOIN schema2.table5 s2t5 ON s2t5.table3_id = s1t3.id
LEFT JOIN schema2.table6 s2t6 ON s2t6.id = s2t5.table6_id
WHERE s1t1.id = :ID
I understand that, when transformation take place the :ID variable will be replaced with the id of the table1 row that I'm getting. The problem I'm having is that field3 and field4 could be NULL in some table7 rows (as you can imagine from the LEFT JOINS in query). So I'm getting the error
Expected a single row, but returned no rows from lookup for target
column field3 on transform source_2_target_table7_table1_schema1
Is there any way to force SymmetricDS to copy a NULL value in this column when lookup expression returns no rows? Or is there any other way to achieve this kind of synchronization?
Thanks in advance

Solved by using BSH transformation and sqlTemplate. Just return null when sqlTemplate query returns no rows.

Related

Retrieve data from table2 using column names stored in table1

I want to be able to look up fields values from a secondary table that stores the secondary table field names as strings in a primary table.
I am trying to both query the primary table itself to get the field names I want from the secondary table:
How can I use that string that contains the field name to select data from table2, given the id of a specific row.
The use case here, is that table1 also contains a note field. I want to populate that note field with the contents of a specific record in table2. I want to do this for all the records in table 1: each record references a different field by field name.
Another way to look at this:
Table2 has 1 row we want data from, and we know the ID
table1 stores the list of fields that we need data for from table 2
How do I get both pieces of data (field name, and field value) using these constraints?
all data is varchar
table1
fieldname
externalID
myField1
001
myField2
001
table2
id
myField1
myField2
myField3
001
myField1ValueForID001
myField2ValueForID001
myField3ValueForID001
002
moredata1
moredata2
moredata3
select fieldname,
(select [fieldname] from table2 where id = ) as fieldData
from table1
result
fieldname
fieldData
myField1
myField1
myField2
myField2
desired result
fieldname
fieldData
myField1
myField1ValueForID001
myField2
myField2ValueForID001
You can do this with a CASE expression, though I do recommend you rethink your design here:
SELECT *
INTO dbo.Table1
FROM (VALUES('myField1','001'),
('myField2','001'))V(FieldName,ExternalID);
SELECT *
INTO dbo.Table2
FROM (VALUES('001','myField1ValueForID001','myField2ValueForID001','myField3ValueForID001'),
('002','moredata1','moredata2','moredata3'))V(id,myField1,myField2,myField3);
GO
SELECT T1.FieldName,
CASE T1.FieldName WHEN 'myField1' THEN T2.myField1
WHEN 'myField2' THEN T2.myField2
WHEN 'myField3' THEN T2.myField3
END AS FieldData
FROM dbo.Table1 T1
JOIN dbo.Table2 T2 ON T1.ExternalID = T2.id;
GO
DROP TABLE dbo.Table1;
DROP TABLE dbo.Table2;

Query Using != On LEFT JOIN Table Returns No Result

I am querying a Microsoft SQL Server 2012.
The primary table (T1) structure contains account details:
AccountID, Name, Address
This table is dropped and recreated using external data nightly. We need to display this information but also need to exclude some of the records. Since we have no access to the external data we can't just add a column.
So we created a table (T2) to mark all the accounts we would like to exclude. It just has 2 fields:
AccountNo, Type
So we populated T2 and for every account we wanted to exclude from the display we gave the Type field a value of 'ex' (for exclude). We have no entries for the account we want to display.
When I execute the following query:
select T1.AccountID as acct, T1.Name as name, T1.Address as add
from T1
left join T2 on T1.AccountID = T2.AccountNo
WHERE T2.Type != 'ex'
The above query returns and empty set.
If I run a query to look for the value 'ex' (remove the !):
select T1.AccountID as acct, T1.Name as name, T1.Address as add
from T1
left join T2 on T1.AccountID = T2.AccountNo
WHERE T2.Type = 'ex'
The query returns the rows with that field populated with 'ex', as you expect.
I can search for NULL or NOT null with success but we need to use this extra table to do some other data manipulation in the future. In other words, we will not just be populating this field with "ex".
I'm wondering why I can't query the field in the joined table by looking for a Boolean false for a string. Is is because since the column doesn't exist in the table that is joined (T2) that it doesn't actually exist in the data set?
If that's the case how would I execute a query to return the records that do not equal a value in the joined table, whether that record exists in the joined table or not.
You can use the ISNULL solution like mentioned in the comments.
Another way you could write the query is this:
SELECT #t1.AccountID AS acct, #t1.Name AS [name], #t1.Address AS [add]
FROM #t1
LEFT JOIN #t2 ON #t1.AccountID = #t2.AccountNo
AND #t2.type = 'ex' --In case you add additional types to #t2
WHERE #t2.AccountNo IS NULL;

2 nvarchar fields are not matching though the data is same?

I want to join 2 tables using an Inner Join on 2 columns, both are of (nvarchar, null) type. The 2 columns have the same data in them, but the join condition is failing. I think it is due to the spaces contained in the column values.
I have tried LTRIM, RTRIM also
My query:
select
T1.Name1, T2.Name2
from
Table1 T1
Inner Join
Table2 on T1.Name1 = T2.Name2
I have also tried like this:
on LTRIM(RTRIM(T1.Name1)) = LTRIM(RTRIM(T2.Name2))
My data:
Table1 Table2
------ ------
Name1(Column) Name2(Column)
----- ------
Employee Data Employee Data
Customer Data Customer Data
When I check My data in 2 tables with
select T1.Name1,len(T1.Name1)as Length1,Datalength(T1.Name1)as DataLenght1 from Table1 T1
select T2.Name2,len(T2.Name2)as Length2,Datalength(T2.Name2)as DataLenght2 from Table2 T2
The result is different Length and DataLength Values for the 2 tables,They are not same for 2 tables.
I can't change the original data in the 2 tables. How can I fix this issue.
Thank You
Joins do not have special rules for equality. The equality operator always works the same way. So if a = b then the join on a = b would work. Therefore, a <> b.
Check the contents of those fields. They will not be the same although you think they are:
select convert(varbinary(max), myCol) from T
Unicode has invisible characters (that only ever seem to cause trouble).
declare #t table (name varchar(20))
insert into #t(name)values ('Employee Data'),('Customer Data')
declare #tt table (name varchar(20))
insert into #tt(name)values ('EmployeeData'),('CustomerData')
select t.name,tt.name from #t t
INNER JOIN #tt tt
ON RTRIM(LTRIM(REPLACE(t.name,' ',''))) = RTRIM(LTRIM(REPLACE(tt.name,' ','')))
I would follow the following schema
Create a new table to store all the possible names
Add needed keys and indexes
Populate with existing names
Add columns to your existing tables to store the index of the name
Create relative foreign keys
Populate the new columns with correct indexes
Create procedure to perform an insert in new tables names only in case the value is not existing
Perform the join using the new indexes

How to get ID from one table and associate with record in another table in SQL Server

I've tried searching for the answer to this one to no avail. There is no good logic behind the way this was setup. The guy does not know what he's doing, but it's what I have to work with (long story).
I'm using SQL Server 2008R2 I need to take records from one table and transfer the data to 4 separate tables all with a one to one relationship (I know - not smart). I need to get the value from the Identity field in the first table the data is inserted into, then populate the other 3 tables with the same ID and disperse the data accordingly. for example:
OldTable: Field1, Field2, Field3, Field4
NewTable1: Identity field, Field1
NewTable2: ID, Field2
NewTable3: ID, Field3
NewTable4: ID, Field4
I'd like to handle this in a stored procedure. I'd like to do a loop, but I read that loops in SQL are inadvisable.
Loop moving through each record in OldTable... (??)
INSERT INTO NewTable1
(Field1)
Select Field1 from OldTable
INSERT INTO NewTable2
(ID, Field2)
Select SCOPE_IDENTITY?, Field2 From OldTable Where OldTable.ID = ??
etc for other 2 tables
Loop to next record in OldTable
I am not sure how to use SCOPE_IDENTITY, but I have a feeling this will be involved in how I accomplish this.
Also, I'm probably going to need to setup a trigger for whenever a new record is created in NewTable1. I know, it's insanity, but I can't do anything about it, just have to work around it.
So, I need to know
1: the best way to initially populate the tables
2: how to make triggers for new records
The solution to 1 might involve 2.
Please help!
You can use the output clause of the merge statement to get a mapping between the existing primary key in OldTable and the newly generated identity ID in NewTable1.
-- Temp table to hold the mapping between OldID and ID
create table #ID
(
OldID int primary key,
ID int
);
-- Add rows to NewTable1 and capture the ID's in #ID
merge NewTable1 as T
using OldTable as S
on 1 = 0
when not matched by target then
insert(Field1) values(S.Field1)
output S.ID, inserted.ID into #ID(OldID, ID);
-- Add rows to NewTable2 using #ID to get the correct value for each row
insert into NewTable2(ID, Field2)
select I.ID, O.Field2
from #ID as I
inner join OldTable as O
on I.OldID = O.ID
insert into NewTable3(ID, Field3)
select I.ID, O.Field3
from #ID as I
inner join OldTable as O
on I.OldID = O.ID
insert into NewTable4(ID, Field4)
select I.ID, O.Field4
from #ID as I
inner join OldTable as O
on I.OldID = O.ID
drop table #ID;
SQL Fiddle
See also Using merge..output to get mapping between source.id and target.id
How about using the OUTPUT clause of the insert statement? Assuming that Field1 is a unique key on the OldTable...
Declare #IDinserted table(ID int, Field1 varchar(255));
Insert Into NewTable1(Field1)
Output inserted.ID, inserted.Field1 into #IDinserted
Select OldID, Field1 from OldTable;
Insert Into NewTable2(RowID, Field2)
Select i.ID, o.#Field2
from #IDinserted i Inner Join OldTable o
on i.Field1=o.Field1;

Merging Two Tables in SQL Server

I have two tables, each with some columns that are the same. However, each table also contains data that is unique. (Similar data includes a row name).
What I need to do is tack on the data from table two to it's matching row in table one (matching the name column).
Is there any way to do this?
I need stuff from table two to go into table 1 where the names match:
The following query should return all matching rows with columns from both tables. Note that any unique rows (that only exist in table one or two) will be excluded.
SELECT
one.matchingColum,
one.oddColum,
two.evenColumn
FROM one
JOIN two on one.matchingColumn = two.matchingColumn
If the data types are the same, then you can do a union
SELECT *
FROM table1
UNION
SELECT *
FROM table2
If the datatypes are not the same and you have a field that you can JOIN on, then you can do a JOIN
SELECT *
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.id

Resources