SQL Inner Join: Weird Result - sql-server

I have data that looks like this:
Table_A:
Source tableName systemid
A table_1 123abcA2
B table_1 222DEFD3
C table_1 369CCCB3
Table_B:
Source tableName systemid
Q table_2 123abc
R table_2 222DEF
C table_2 369CCC
I ran the following query:
select a.Source, a.tableName, a.systemid as a_systemid, b.systemid as b_systemid
from table_a as a
inner join table_b as b on a.systemid = b.systemid
Here is what came back:
Source tableName a_systemid b_systemid
A table_1 123abcA2 123abc
B table_1 222DEFD3 222def
C table_1 369CCCB3 369CCC
Shouldn't I get nothing returned? As nothing matches.
Table A system id = nvarchar data type
Table b systemid = uniqueidentifier data type

Implicit casting is occuring and essentially truncating your string data which causes a match. You need to explicitly cast the uniqueidentifier to an nvarchar(max).
Please see this question and answer.
DECLARE #t1 TABLE([Source] CHAR(1),tableName VARCHAR(10),systemid nvarchar(max))
DECLARE #t2 TABLE([Source] CHAR(1),tableName VARCHAR(10),systemid uniqueidentifier)
INSERT INTO #t1 SELECT 'A','table_1','15b993cc-e8be-405d-bb9f-0c58b66dcdfe_1'
INSERT INTO #t1 SELECT 'B','table_1','4cffe724-3f68-4710-b785-30afde5d52f8_1'
INSERT INTO #t1 SELECT 'C','table_1','7ad22838-ddee-4043-8d1f-6656d2953545_1'
INSERT INTO #t2 SELECT 'Q','table_2','15b993cc-e8be-405d-bb9f-0c58b66dcdfe'
INSERT INTO #t2 SELECT 'R','table_2','4cffe724-3f68-4710-b785-30afde5d52f8'
INSERT INTO #t2 SELECT 'C','table_2','7ad22838-ddee-4043-8d1f-6656d2953545'
select a.Source, a.tableName, a.systemid as a_systemid, b.systemid as b_systemid
from #t1 as a
inner join #t2 as b on a.systemid = CONVERT(NVARCHAR(MAX),b.systemid)
As a practice, you should always explicitly cast mismatched datatypes for clarity as well as prevent weird "what is going on?!?!" stuff.

Related

PATINDEX all values of a column

I'm making a query that will delete all rows from table1 that has its column table1.id = table2.id
table1.id column is in nvarchar(max) with an xml format like this:
<customer><name>Paulo</name><gender>Male</gender><id>12345</id></customer>
EDIT:
The id column is just a part of a huge XML so the ending tag may not match the starting tag.
I've tried using name.nodes but it only applies to xml columns and changing the column datatype is not a choice, So far this is the my code using PATINDEX
DELETE t1
FROM table1 t1
WHERE PATINDEX('%12345%',id) != 0
But what I need is to search for all values from table2.id which contains like this:
12345
67890
10000
20000
30000
Any approach would be nice like sp_executesql and/or while loop, or is there a better approach than using patindex? thanks!
Select *
--Delete A
From Table1 A
Join Table2 B on CharIndex('id>'+SomeField+'<',ID)>0
I don't know the name of the field in Table2. I am also assuming it is a varchar. If not, cast(SomeField as varchar(25))
EDIT - This is what I tested. It should work
Declare #Table1 table (id varchar(max))
Insert Into #Table1 values
('<customer><name>Paulo</name><gender>Male</gender><id>12345</id></customer>'),
('<customer><name>Jane</name><gender>Femail</gender><id>7895</id></customer>')
Declare #Table2 table (SomeField varchar(25))
Insert into #Table2 values
('12345'),
('67890'),
('10000'),
('20000'),
('30000')
Select *
--Delete A
From #Table1 A
Join #Table2 B on CharIndex('id>'+SomeField+'<',ID)>0
;with cteBase as (
Select *,XMLData=cast(id as xml) From Table1
)
Select *
From cteBase
Where XMLData.value('(customer/id)[1]','int') in (12345,67890,10000,20000,30000)
If you are satisfied with the results, change the final Select * to Delete

SQL Server 2008 throws "Declare table variable" errors

SQL Server 2008 throws errors when trying to write following query to create a stored procedure:
DECLARE #T table (TimeId uniqueIdentifier)
INSERT INTO #T
SELECT DISTINCT ref1.value( './#v', 'uniqueIdentifier')
FROM #xmlTimeIds.nodes('/a/i') T(ref1)
UPDATE TAB1
SET TAB1.TMAT_INVOICED_HOURS = 0
FROM dbo.TMAT_TimeRegisterAttribute AS TAB1
INNER JOIN #T on TAB1.TMAT_AT_GUID = #T.TimeId
WHERE TAB1.TMAT_TN_GUID = #guidTenantId
Give #T an alias in the update sql. The compiler regards #T as a variable rather than a table.
DECLARE #T table (TimeId uniqueIdentifier)
INSERT INTO #T
SELECT DISTINCT ref1.value( './#v', 'uniqueIdentifier')
FROM #xmlTimeIds.nodes('/a/i') T(ref1)
UPDATE TAB1
SET TAB1.TMAT_INVOICED_HOURS = 0
FROM dbo.TMAT_TimeRegisterAttribute AS TAB1
INNER JOIN #T TempTable on TAB1.TMAT_AT_GUID = TempTable.TimeId
WHERE TAB1.TMAT_TN_GUID = #guidTenantId
You have on repeated twice: INNER JOIN #T on on TAB1.TMAT_AT_GUID = #T.TimeId. Should be like this:
DECLARE #T table (TimeId uniqueIdentifier)
INSERT INTO #T
SELECT DISTINCT ref1.value( './#v', 'uniqueIdentifier')
FROM #xmlTimeIds.nodes('/a/i') T(ref1)
UPDATE TAB1
SET TAB1.TMAT_INVOICED_HOURS = 0
FROM dbo.TMAT_TimeRegisterAttribute AS TAB1
INNER JOIN #T on TAB1.TMAT_AT_GUID = #T.TimeId
WHERE TAB1.TMAT_TN_GUID = #guidTenantId

SQL Server: Multiple Output Clauses

I have two tables, Table_1 and Table_2.
Table_1 has columns PK (autoincrementing int) and Value (nchar(10)).
Table_2 has FK (int), Key (nchar(10)) and Value (nchar(10)).
That is to say, Table_1 is a table of data and Table_2 is a key-value store where one row in Table_1 may correspond to 0, 1 or more keys and values in Table_2.
I'd like to write code that programmatically builds up a query that inserts one row into Table_1 and a variable number of rows into Table_2 using the primary key from Table_1.
I can do it easy with one row:
INSERT INTO Table_1 ([Value])
OUTPUT INSERTED.PK, 'Test1Key', 'Test1Val' INTO Table_2 (FK, [Key], [Value])
VALUES ('Test')
But SQL doesn't seem to like the idea of having multiple rows. This fails:
INSERT INTO Table_1 ([Value])
OUTPUT INSERTED.PK, 'Test1Key', 'Test1Val' INTO Table_2 (FK, [Key], [Value])
OUTPUT INSERTED.PK, 'Test2Key', 'Test2Val' INTO Table_2 (FK, [Key], [Value])
OUTPUT INSERTED.PK, 'Test3Key', 'Test3Val' INTO Table_2 (FK, [Key], [Value])
VALUES ('Test')
Is there any way to make this work?
I had to put the code in answer, in comment it looks ugly...
CREATE TABLE #Tmp(PK int, value nchar(10))
INSERT INTO Table_1 ([Value])
OUTPUT INSERTED.PK, inserted.[Value] INTO #Tmp
SELECT 'Test'
INSERT INTO Table_2 (FK, [Key], Value)
SELECT PK, 'Test1Key', 'Test1Val' FROM #Tmp
UNION ALL SELECT PK, 'Test2Key', 'Test2Val' FROM #Tmp
UNION ALL SELECT PK, 'Test3Key', 'Test3Val' FROM #Tmp
Btw, SQL Server won't let you do it all in one query without some ugly hack...
Try putting the INSERTED.PK value into a parameter, then inserting into table 2 with 3 INSERT..VALUES or 1 INSERT..SELECT statement.

What is the difference when adding a filter criteria to an outer join instead of a where clause?

I'm trying to filter out a table using filter in the outer join clause rather than a where clause. When i try to do so i'm getting unexpected results. The whole table is returned as though i didn't apply the filter at all.
When I run this example, i get different results for the last two queries. I would expect them to have the same results but it's not the case. What is going on here?
declare #a table
(
id int
,content varchar(100)
)
declare #b table
(
id int
,content varchar(100)
)
insert into #a (id,content) values (1,'Apple')
insert into #a (id,content) values (2,'Banana')
insert into #a (id,content) values (3,'Orange')
insert into #b (id,content) values (1,'Juice')
insert into #b (id,content) values (2,'Peel')
insert into #b (id,content) values (3,'Julius')
--basic outer join
select * from #a a left join #b b on a.id=b.id
--outer join with where clause filter
select * from #a a left join #b b on a.id=b.id where a.id=1
--outer join with join clause filter
select * from #a a left join #b b on a.id=1 and a.id=b.id
An outer join is allowed to return NULL for the joined table's row, whereas a where clause must be matched before the result is returned.
select * from #a a left join #b b on a.id=b.id where a.id=1
translates to "Give me all rows from a where id=1 and try to correlate this with any rows in b where a.id=b.id.
select * from #a a left join #b b on a.id=1 and a.id=b.id
on the other hand, translates to "Give me all rows from a and, if a.id=1, try to correlate this with any rows in b where a.id=b.id (otherwise just give me the data from a).
Contrast this with an inner join, where adding a condition to the ON clause and adding it to the WHERE clause is synonymous.

Update between two tables when there is no relation between them

I need a SQL statement which fills the null values from the second column of #T1 table with values from #T2(C1).
There is no foreign key or match between the columns of those two tables.
Sample:
T1 (C1, T2C1)
A1, 1
A2, null
A3, null
A4, 4
A5, null
-------------
T2 (C1)
a
b
After update, the T1 will look like:
A1, 1
A2, a
A3, b
A4, 4
A5, null
I found two approaches:
Using CTE
create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))
insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)
insert into #T2 values ('a')
insert into #T2 values ('b')
;with t2 as
(
select C1, row_number() over (order by C1) as Index2
from #T2
)
,t1 as
(
select T2C1, row_number() over (order by C1) as Index1
from #T1
where T2C1 is null
)
update t1
set t1.T2C1 = t2.C1
from t2
where t1.Index1 = t2.Index2
select * from #T1
drop table #T1
drop table #T2
With Derived Tables
create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))
insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)
insert into #T2 values ('a')
insert into #T2 values ('b')
update #T1
set T2C1 = cj.C1
from #T1
join (select T2C1, row_number() over (order by C1) as Index1, C1
from #T1
where T2C1 is null) ci on ci.C1 = #T1.C1
join (select C1, row_number() over (order by C1) as Index2
from #T2) cj on ci.Index1 = cj.Index2
select * from #T1
drop table #T1
drop table #T2
My question is, can I achieve this without using windowing functions and with no cursors?
Update
#Damien_The_Unbeliever correctly points that to do this kind of update it is not possible without defining an ordering on tables, actually I think exactly said is without properly identify and link the rows from target table.
#Bogdan Sahlean has found another way, using table variables and IDENTITY column, which I'm happy with this solution, it's another way
However, in the real application I will still use the windowing functions
Thanks all
1.I suppose you have a pk in target table (#T1).
2.Instead of ROW_NUMBER this solution uses IDENTITY(1,1) columns and two table variables.
3.I didn't tested this solution.
DECLARE #t2_count INT = (SELECT COUNT(*) FROM #T2);
DECLARE #Target TABLE
(
MyId INT IDENTITY(1,1) PRIMARY KEY
,T1_pk INT NOT NULL UNIQUE
);
INSERT #Target (T1_pk)
SELECT TOP(#t2_count) pk
FROM #T1
WHERE T2C1 IS NULL;
DECLARE #Source TABLE
(
MyId INT IDENTITY(1,1) PRIMARY KEY
,C1 VARCHAR(10) NOT NULL
);
INSERT #Source (C1)
SELECT C1
FROM #T2;
UPDATE #T1
SET T2C1 = src.C1
FROM #T1 t
INNER JOIN #Target trg ON t.pk = trg.T1_pk
INNER JOIN #Source src ON trg.MyId = src.MyId;
I agree with Damien, anyway when you will rely on the engine and make a premise that the table is ordered by the C1 column (which depends only on the DB and you can not rely on that ) the you could issue an update statement which will update all the rows
declare #a int
set #a = 0
update #t1
set t2c1 = #a,
#a = #a+1
But I wouldn't do that.
I would use a CTE along with ROW_NUMBER:
WITH cte_t1 AS
(
SELECT
T2C1
,ROW_NUMBER() OVER (ORDER BY C1) AS id
FROM T1
WHERE T2C1 IS NULL
)
,cte_t2 AS
(
SELECT
C1
,ROW_NUMBER() OVER (ORDER BY C1) AS id
FROM T2
)
UPDATE t1
SET t1.T2C1 = t2.C1
FROM cte_t1 AS t1
INNER JOIN cte_t2 AS t2
ON t1.id = t2.id
;
The ROW_NUMBER creates an identifier based on the order the columns are in (which I am assuming is what you are looking for, but note that SQL does not have an order instead this query is relying on the physical order of the records which is not a good idea but it looks like that is what you are dealing with). We then join on this identifier and update the records in T1 with the values in T2.
I have placed this solution on SQL Fiddle.
If you can look into create new tables with some type of identifier that links them together, then you would not have to rely on the physical order of the records.

Resources