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 |
Related
I have a table with rows and in one field there are values like this A,B,C
Table 'Mytable':
|ID | Date | MyValue | SplitID |
|1 | 2019-12-17 | A | |
|2 | 2019-12-15 | A,B | |
|3 | 2019-12-16 | B,C | |
Result should be:
|1 | 2019-12-17 | A | 1 |
|2 | 2019-12-15 | A | 2 |
|4 | 2019-12-15 | B | 2 |
|3 | 2019-12-16 | B | 3 |
|5 | 2019-12-16 | C | 3 |
(Sorry, I could not find HOW to format a table in the Stackoverflow help)
I tried a inline table function which splits the Field Myvalue into more lines but could not pass my rows with
charindex(',',[MyValue])>0
from MyTable as input lines.
The code is this:
ALTER function [dbo].[fncSplitString](#input Varchar(max), #Splitter Varchar(99), #ID int)
returns table as
Return
with tmp (DataItem, ix, ID) as
( select LTRIM(#input) , CHARINDEX('',#Input), #ID --Recu. start, ignored val to get the types right
union all
select LTRIM(Substring(#input, ix+1,ix2-ix-1)), ix2, #ID
from (Select *, CHARINDEX(#Splitter,#Input+#Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem,ID from tmp where ix<>0
Thanks for help
Michael
You can try the following query.
Create table #Temp(
Id int,
DateField Date,
MyValue Varchar(10),
SplitID int
)
CREATE FUNCTION [dbo].[SplitPra] (#Value VARCHAR(MAX), #delimiter CHAR)
RETURNS #DataResult TABLE([Position] TINYINT IDENTITY(1,1),[Value] NVARCHAR(128))
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#Value, #delimiter, ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #DataResult ([Value])
SELECT RTRIM(LTRIM(T.c.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') T(c)
RETURN
END
insert into #Temp Values(1, '2019-12-17', 'A', NULL),(2, '2019-12-15', 'A,B', NULL), (3, '2019-12-16', 'B,C', NULL)
Select
#Temp.Id, DateField, b.Value as MyValue, b.Id as SplitValue
from #Temp inner join (
select
Id, f.*
from
#Temp u
cross apply [dbo].[SplitPra](u.MyValue, ',') f
)b on #Temp.Id = b.Id
Drop table #Temp
This will give an output as shown below.
Id DateField MyValue SplitValue
---------------------------------
1 2019-12-17 A 1
2 2019-12-15 A 2
2 2019-12-15 B 2
3 2019-12-16 B 3
3 2019-12-16 C 3
You can find the live demo here.
I found this solution, i hope it will work for you. But i didn't use your function to solve this problem. Instead of that, i used cross apply function.You can find the query below:
-- Creating Test Table
CREATE TABLE #Test
(
ID int,
Date date,
MyValue nvarchar(max),
SplitID int
);
GO
-- Inserting data into test table
INSERT INTO #Test VALUES (1, '2019-12-17', 'A', NULL);
INSERT INTO #Test VALUES (2, '2019-12-15', 'A,B', NULL);
INSERT INTO #Test VALUES (3, '2019-12-16', 'B,C', NULL);
GO
-- Select query
SELECT
*,
(SELECT ID FROM test t1 WHERE t.Date = t1.date) AS SplitID
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS ID,
Date,
substring(A.value,1,
CASE WHEN charindex(',',rtrim(ltrim(A.value))) = 0 then LEN(A.value)
ELSE charindex(',',rtrim(ltrim(A.value))) -1 end) as MyValue
FROM Test
CROSS APPLY string_split (MyValue, ',') A) AS T
ORDER BY MyValue ASC;
And the result must be like that:
ID Date MyValue SplitID
1 2019-12-17 A 1
2 2019-12-15 A 2
3 2019-12-15 B 2
4 2019-12-16 B 3
5 2019-12-16 C 3
Basically I have 2 Tables, the first with the raw material amount (QT) for each serial number and the second one with how much raw material was spent (Qt_Added) on batch's production. Like this:
Table 1
+----------+------------+-----+
| Code_Raw | Serial_Raw | Qt |
+----------+------------+-----+
| 1 | 1 | 100 |
| 1 | 2 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
+----------+------------+-----+
And Table 2
+------------+----------+------------+----------+--+
| Code_Batch | Code_Raw | Serial_Raw | Qt_Added | |
+------------+----------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------+----------+------------+----------+--+
I tried to do a query for a specific Code_Raw, show me how much left for each serial number, But worked only when there's a single serial_raw.
My query:
select *
from
(select
Serial_Raw,
(Select QT From Table_1 where Code_Raw = 1) - Sum(qt_added) as Total_Remaining
from
Table_2
where
Cod_Raw = 1
group by
Serial_Raw) e
where
Total_Remaining > 0
but it throws this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression
And I expect :
Serial_Raw Total_Remaining
-------------------------------
1 10
3 20
Is there a struct problem or another way to do this?
I'm using SQL Server 2014
Thanks guys
Try this:
DECLARE #tbl1 TABLE
( CodeRaw INT,
Serial_Raw INT,
Qty INT)
DECLARE #tbl2 TABLE
(
CodeBatch INT,
CodeRaw INT,
Serial_Raw INT,
QtyAdded INT)
INSERT INTO #tbl1 VALUES(1,1,100)
INSERT INTO #tbl1 VALUES(1,2,150)
INSERT INTO #tbl1 VALUES(2,1,80)
INSERT INTO #tbl1 VALUES(1,3,100)
INSERT INTO #tbl2 VALUES(1,1,1,80)
INSERT INTO #tbl2 VALUES(2,1,1,10)
INSERT INTO #tbl2 VALUES(3,1,2,150)
INSERT INTO #tbl2 VALUES(4,1,3,80)
--Inner table has the summary of the Quantity added with columns CodeRaw and SerialRaw. Outer table make join with inner table and just substruct with the Qty and Sum of Qty Added.
SELECT t2.Serial_Raw, t1.Qty - t2.QtyAdded AS Total_Remaining FROM #tbl1 t1
INNER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.Qty - t2.QtyAdded > 0
If I understood you right, this might be what you are after
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
select t2.Serial_Raw,
t3.Qty - sum(t2.QtyAdded) as Total_Remaining
from #tbl2 t2
inner join ( select t1.Serial_Raw,
t1.CodeRaw,
sum(t1.Qty) as Qty
from #tbl1 t1
group by t1.Serial_Raw, t1.CodeRaw
) t3
on t2.Serial_Raw = t3.Serial_Raw
and t2.CodeRaw = t3.CodeRaw
group by t2.Serial_Raw, t3.Qty
So in t2 we get all distinct Serial_Raw values, and sum their QtyAdded from the first table.
In t3 we get all Qty values from the 2nd table.
All we need to do is join them together and subtract
The result of this query is
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20
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
I am trying to join two tables together. The first table contains data records that I do not want to duplicate. The second table I am joining to the first table to lookup a [value] by a distinct [profileId] and [role]. The [profileId], [role] column in the second table has a unique constraint on the combination, but [role] can sometimes be NULL, in which case I treat that value as the default for that profile.
How can I join these tables together without duplicating the rows, and without using multiple left joins? My actual query is more complex than the example.
See example below.
DECLARE #temp TABLE ([profileId] int, [role] int)
DECLARE #temp2 TABLE ([profileId] int, [role] int, [value] nvarchar(50))
INSERT INTO #temp ([profileId], [role]) VALUES (1, 1)
INSERT INTO #temp ([profileId], [role]) VALUES (1, 2)
INSERT INTO #temp ([profileId], [role]) VALUES (2, 1)
INSERT INTO #temp ([profileId], [role]) VALUES (2, 2)
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (1, 1, 'MATCH')
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (1, NULL, 'DEFAULT1')
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (2, NULL, 'DEFAULT2')
SELECT
T1.[profileId],
T1.[role],
T2.value
FROM
#temp T1
JOIN #temp2 T2 ON T1.profileId = T2.profileId AND COALESCE(T2.[role], T1.[role]) = T1.[role]
This gives me (and I understand why)
================================
| profileId | role | value |
================================
| 1 | 1 | MATCH |
--------------------------------
| 1 | 1 | DEFAULT1 |
--------------------------------
| 1 | 2 | DEFAULT1 |
--------------------------------
| 2 | 1 | DEFAULT2 |
--------------------------------
| 2 | 2 | DEFAULT2 |
================================
While I want
================================
| profileId | role | value |
================================
| 1 | 1 | MATCH |
--------------------------------
| 1 | 2 | DEFAULT1 |
--------------------------------
| 2 | 1 | DEFAULT2 |
--------------------------------
| 2 | 2 | DEFAULT2 |
================================
This SQL works fine:
SELECT
T1.[role],
Value = coalesce(max(nullif(T2.value,'DEFAULT')),'DEFAULT')
FROM
#temp T1
JOIN #temp2 T2 ON COALESCE(T2.[role], T1.[role]) = T1.[role]
group by
T1.[role]
;
You can use APPLY and TOP:
SELECT
t.profileId,
t.role,
x.value
FROM #temp t
OUTER APPLY(
SELECT TOP 1 value
FROM #temp2
WHERE
profileId = t.profileId
AND (role = t.role OR role IS NULL)
ORDER BY
CASE WHEN role IS NOT NULL THEN 0 ELSE 1 END
)x
if you know the DEFAULT value use left join.
SQL Fiddle Demo
SELECT
T1.[role],
COALESCE(T2.value, 'DEFAULT') as value
FROM
temp T1
LEFT JOIN temp2 T2
ON T1.[role] = T2.[role];
Otherwise
SELECT
T1.[role],
COALESCE(T2.value, (SELECT value
FROM temp2
WHERE role is NULL and temp2.profileID = T1.profileID)) as value
FROM
temp T1
LEFT JOIN temp2 T2
ON T1.[role] = T2.[role]
AND T1.[profileID] = T2.[profileID]
;
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 |