SQL server and STUFF with two tables - sql

I'm facing a problem. I have two tables as below.
table 1
+----+------+
| ks | keys |
+----+------+
| 11 | 1122|
+----+------+
| 12 | 2211|
+----+------+
| 13 | 2233|
+----+------+
| 14 | 3322|
+----+------+
table 2
+----+--+-------+
| Id | ks|codes|
+----+-----------+
| 1 | 11 |aaaaa|
+----+-----------+
| 2 | 11 |bbbbb|
+----+-----------+
| 3 | 12 |aaaaa|
+----+-----------+
| 3 | 13 |ccccc|
+----+-----------+
| 4 | 12 |bbbbb|
+----+-----------+
I tried to implement a following query in order to get my required output but did not work:
SELECT ks,
STUFF (
(SELECT ', ' + t2.codes as [text()]
from table2 as t2 where t1.ks = t2.ks FOR XML PATH('')
),1,1,''
) as "codes"
from table1 t1
group by ks;
I get this table as result:
+----+------+
| ks | codes|
+----+------+
| 11 | aaaa |
+----+------+
| 11 | bbbb |
+----+------+
| 12 | cccc |
+----+------+
| 12 | dddd |
+----+------+
then this image below shows my required output:
required result
I did something wrong but I do not know what could be. Any chance someone help me? Thanks!

Try this. I think you posted the wrong output.
Create table #tbl (ks int , codes varchar(10))
Insert into #tbl values
(11 ,'aaaa'),
(12 ,'bbbb'),
(13 ,'cccc'),
(14 ,'dddd')
Create table #tbl2 (id int, ks int , codes varchar(10))
Insert into #tbl2 values
( 1 ,11 ,'aaaaa'),
( 2 ,11 ,'bbbbb'),
( 3 ,12 ,'aaaaa'),
( 3 ,13 ,'ccccc'),
( 4 ,12 ,'bbbbb')
with cte as
(Select t1.ks, t2.codes
from #tbl t1 join #tbl2 t2 on t1.ks = t2.ks)
Select ks, STUFF(
(SELECT ',' + codes FROM cte c1
where c1.ks = c2.ks FOR XML PATH ('')), 1, 1, ''
)
from cte c2
group by ks
Output:
ks
11 aaaaa,bbbbb
12 aaaaa,bbbbb
13 ccccc

I cannot say that I fully understand what is going on in your tables--especially given your output image appears to have no relation to your sample tables--but it looks like you want a comma-delimited list of sub-values from table2 that are associated with table1.
Here's a working example that I think addresses your need. You can use CROSS APPLY in these situations. Doing so allows you to return all values from table1 regardless of a matching record in table2.
DECLARE #table1 TABLE ( [ks] INT, [code] VARCHAR(10) );
DECLARE #table2 TABLE ( [id] INT, [ks] INT, [code] VARCHAR(10) );
-- populate table1 --
INSERT INTO #table1 (
[ks], [code]
)
VALUES
( 11, 'aaaa' )
, ( 12, 'bbbb' )
, ( 13, 'cccc' )
, ( 14, 'dddd' );
-- populate table two --
INSERT INTO #table2 (
[id], [ks], [code]
)
VALUES
( 1, 11, 'aaaaa' )
, ( 2, 11, 'bbbbb' )
, ( 3, 12, 'aaaaa' )
, ( 3, 13, 'ccccc' )
, ( 4, 12, 'bbbbb' );
SELECT
t1.ks, codes.codes
FROM #table1 t1
CROSS APPLY (
SELECT (
STUFF(
( SELECT ', ' + t2.code AS "text()" FROM #table2 t2 WHERE t2.ks = t1.ks FOR XML PATH ( '' ) )
, 1, 2, ''
)
) AS [codes]
) AS codes
ORDER BY
t1.ks;
Resulting Output:
ks codes
11 aaaaa, bbbbb
12 aaaaa, bbbbb
13 ccccc
14 NULL

Related

Adding new data in columns, from another table - SQL ORACLE

I have a table with some records
MASTER TABLE
x------x--------------------x-------x
| Id | PERIOD | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 2 | 2014-01-06 | 30 |
x------x--------------------x-------x
I have another table with parameters of this record (ID)
TABLE2
x------x--------------------x------------x
| Id | Parameter | Value |
x------x--------------------x------------x
| 1 | Humidty | 10 |
| 1 | Temperature | 30 |
| 2 | Humidty | 50 |
| 2 | Temperature | 40 |
x------x--------------------x------------x
As result I want this: (combine based on ID)
Result table
x------x--------------------x-------------------------x
| Id | Period | Humidty | Temperature |
x------x--------------------x-------------------------x
| 1 | 2014-01-13 | 10 | 30 |
| 2 | 2014-01-06 | 50 | 40 |
x------x--------------------x-------------------------x
How Can I do something like that? Inner join will not work I think.
Join the tables and use conditional aggregation with case to extract the 2 columns:
select t1.id, t1.period,
max(case when t2.parameter = 'Humidty' then t2.value end) Humidty,
max(case when t2.parameter = 'Temperature' then t2.value end) Temperature
from mastertable t1 inner join table2 t2
on t2.id = t1.id
group by t1.id, t1.period
You can pivot:
SELECT * FROM
(
SELECT
t_m.Id
, t_m.Period
, t_2.Parameter
, t_2.Value
FROM #tbl_Master t_m
INNER JOIN #tbl_2 t_2 ON t_2.Id = t_m.Id
)AS t
PIVOT
(
MAX(t.Value)
FOR t.Parameter IN ([Humidity], [Temperature])
)pvt
and sample data:
DECLARE #tbl_Master TABLE
(
Id int,
Period Date,
QTY int
)
DECLARE #tbl_2 TABLE
(
Id int,
Parameter varchar(30),
[Value] int
)
INSERT INTO #tbl_Master
(
Id,
Period,
QTY
)
VALUES
(1, '2014-01-13', 10)
, (2, '2014-01-06', 30)
INSERT INTO #tbl_2
(
Id ,
Parameter ,
[Value]
)
VALUES
( 1, 'Humidity', 10)
, ( 1, 'Temperature' , 30)
, ( 2, 'Humidity', 50)
, ( 2, 'Temperature' , 40)
OUTPUT:
Id Period Humidity Temperature
1 2014-01-13 10 30
2 2014-01-06 50 40
Try this
DECLARE #Mastertable AS TABLE(Id INT,PERIOD DATE,QTY INT)
INSERT INTO #Mastertable
SELECT 1 ,'2014-01-13', 10 UNION ALL
SELECT 2 ,'2014-01-06', 30
DECLARE #Childtable AS TABLE(Id INT,Parameter VARCHAR(100), Value INT)
INSERT INTO #Childtable
SELECT 1 ,'Humidty' , 10 UNION ALL
SELECT 1 ,'Temperature' , 30 UNION ALL
SELECT 2 ,'Humidty' , 50 UNION ALL
SELECT 2 , 'Temperature' , 40
SELECT Id,Period,[Humidty],[Temperature]
FROM
(
SELECT c.Id,
m.PERIOD,
Parameter,
c.Value
FROM #Mastertable m
INNER JOIN #Childtable c
ON m.Id = c.Id
) AS srC
pivot
(MAX(Value) FOR Parameter IN ([Humidty],[Temperature])
) AS PVT
Result
Id Period Humidty Temperature
----------------------------------
1 2014-01-13 10 30
2 2014-01-06 50 40

Cache SQL query to create 1 row from multiple records

I have the below record and would like to create 1 row record.
I tried STUFF, FOR XML PATH and did not work
+-----------+-------+---------+
| CLIENT_ID | Event | DX_Code |
+-----------+-------+---------+
| 54 | 5 | F45.72 |
| 54 | 5 | X45.34 |
| 54 | 5 | M98.32 |
+-----------+-------+---------+
Output = 54, 5, F45.72 X45.34 M98.32
You can do that by using STUFF() with FOR XML PATH('') as
CREATE TABLE T
([CLIENT_ID] int, [Event] int, [DX_Code] varchar(6))
;
INSERT INTO T
([CLIENT_ID], [Event], [DX_Code])
VALUES
(54, 5, 'F45.72'),
(54, 5, 'X45.34'),
(54, 5, 'M98.32')
;
SELECT DISTINCT T1.[CLIENT_ID],
T1.[Event],
STUFF(
(
SELECT ',' + T2.[DX_Code]
FROM T T2
WHERE T2.[CLIENT_ID] = T1.[CLIENT_ID]
AND T2.[Event] = T1.[Event]
FOR XML PATH ('')
) , 1, 1, ''
) Result
FROM T T1;
This should give you the expected result
SELECT CAST(t1.CLIENT_ID AS VARCHAR) + ','+ CAST(t1.Event AS VARCHAR)+ ','+
STUFF(( SELECT ' ' + t2.DX_Code AS [text()]
FROM #temp t2
WHERE
t2.CLIENT_ID = t1.CLIENT_ID
and t2.Event = t1.Event
FOR XML PATH('')
), 1, 1, '' )
AS OutputText
FROM #temp t1
GROUP BY t1.CLIENT_ID,t1.Event
Output:
54,5,F45.72 X45.34 M98.32

Joining tables containing comma delimited values

I have three excel sheet I push them into tables in SQL server and I need to join these table. However, I believe - as I have tried already - normal join wouldn't work. I have programming background but not that much with SQL.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
2 some data 9523
3 some data ss-952
4 some data null
Table2
ID Data_column
ss-456 some data
ss-952 some data
Table3
ID Data_column
1528 some data
9523 some data
In the case below How I will be able to join this raw on both table.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id varchar(20)
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
Quick solution
select * from #t1 t1
left outer join #t2 t2 on t1.reference_number like '%'+t2.id or t1.reference_number like t2.id+'%'
left outer join #t3 t3 on t1.reference_number like '%'+t3.id or t1.reference_number like t3.id+'%'
Result (left join):
id data_column reference_number id data_column id data_column
1 some data 1528,ss-456 ss-456 some data 1528 some data
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data NULL NULL NULL NULL NULL
You can change 'left outer join' to 'inner join' for exact match.
Clumsy design, clumsy solution:
SELECT *
FROM Table1
INNER JOIN Table2 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table2.ID + ',%'
INNER JOIN Table3 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table3.ID + ',%'
You must append leading and trailing commas to make sure that, for example, 1528,ss-456asdf does not match %ss-456%.
I see two problems here. First is the inconsistent type of ID in table 2 and 3 and aggregation of referenced keys in table 1. Here is an example how to solve both problems. To split REFERENCE_NUMBER column I used STRING_SPLIT function.
Update:
I added the solution which should work with SQL Server 2012.
I assumed that you wish to join data from table 1 with 2 or 3 depending in existence of this data. This is just my idea what you wanted to achive.
-- data preparing
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id int
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
-- Solution example version >= 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply string_split(t1.reference_number,',') f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
-- Solution for SQL Version < 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply(
SELECT Split.a.value('.', 'NVARCHAR(MAX)') value
FROM
(
SELECT CAST('<X>'+REPLACE(t1.reference_number, ',', '</X><X>')+'</X>' AS XML) AS String
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
) f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
You will require a function to divide the comma separated sting into rows. If you don't have access to thr inbuilt string_split() function (as of mssql 2017 with compatibility of 130) there are several to choose from here
CREATE TABLE table1(
ID INTEGER NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
,reference_number VARCHAR(11)
);
INSERT INTO table1(ID,Data_column,reference_number) VALUES
(1,'t1somedata','1528,ss-456')
, (2,'t1somedata','9523')
, (3,'t1somedata','ss-952')
, (4,'t1somedata',NULL);
CREATE TABLE table2(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table2(ID,Data_column) VALUES
('ss-456','t2somedata'),
('ss-952','t2somedata');
CREATE TABLE table3(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table3(ID,Data_column) VALUES
('1528','t3somedata'),
('9523','t3somedata');
I have used this splistring function, but you can use almost any of the many freely available.
CREATE FUNCTION dbo.SplitStrings_Moden
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(#List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(#List,t.N,1) = #Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(#List, s.N1, ISNULL(NULLIF(CHARINDEX(#Delimiter,#List,s.N1),0)-s.N1,8000))
FROM cteStart s;
This is what the data looks like using the splitstring function:
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
ID | Data_column | reference_number | Item
-: | :---------- | :--------------- | :-----
1 | t1somedata | 1528,ss-456 | 1528
1 | t1somedata | 1528,ss-456 | ss-456
2 | t1somedata | 9523 | 9523
3 | t1somedata | ss-952 | ss-952
4 | t1somedata | null | null
and now joining to the other tables:
select
*
from (
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
) t1
left join table2 on t1.item = table2.id
left join table3 on t1.item = table3.id
where t1.item is not null
GO
ID | Data_column | reference_number | Item | ID | Data_column | ID | Data_column
-: | :---------- | :--------------- | :----- | :----- | :---------- | :--- | :----------
1 | t1somedata | 1528,ss-456 | 1528 | null | null | 1528 | t3somedata
1 | t1somedata | 1528,ss-456 | ss-456 | ss-456 | t2somedata | null | null
2 | t1somedata | 9523 | 9523 | null | null | 9523 | t3somedata
3 | t1somedata | ss-952 | ss-952 | ss-952 | t2somedata | null | null
db<>fiddle here
TRY THIS: If your reference_number is fixed and always stored IDs upto 2 only then you can go with the below approach
SELECT *
FROM(
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, PATINDEX ( '%,%', reference_number)+1, LEN(reference_number))
ELSE reference_number END AS ref_col
FROM #table1
UNION
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, 0, PATINDEX ( '%,%', reference_number))
END
FROM #table1) t1
LEFT JOIN #table2 t2 ON t2.id = t1.ref_col
LEFT JOIN #table3 t3 ON t3.id = t1.ref_col
WHERE t1.ref_col IS NOT NULL
OUTPUT:
ID data_column ref_col ID Data_column ID Data_column
1 some data 1528 NULL NULL 1528 some data
1 some data ss-456 ss-456 some data NULL NULL
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data null NULL NULL NULL NULL
You can Implement and get desired result using Substring and charIndex functions on the reference_number.
I upvoted an answer of 'is_oz' since i used his ready made schema to test and build a query for you.
below is the final query i build after several tries i made here:
select * from abc
left join abc2 on abc2.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,charindex(',',abc.reference_number)+1
,len(abc.reference_number)-(charindex(',',abc.reference_number)-1)
)
else abc.reference_number
end
left join abc3 on abc3.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,0
,(charindex(',',abc.reference_number))
)
else abc.reference_number
end
As per your requirement as much as i understand, it is returning all the matched rows from 2 other tables but still i hope this fulfills all the requirements you seek in your question. :)

SQL for the below requirements

I am having trouble framing a SQL to get the desired outputs.
Table X
Id_X | GroupId | SomeColumn
Table R
Id_R | Id_X | GroupId | RColumn
The objective is to pick Id_X from Table R that have only GroupId values (A,B) and RColumn value is RValue
Ex:
Table X
1 | A | SomeValue
1 | B | SomeValue
2 | A | SomeValue
2 | B | SomeValue
2 | B | SomeValue
2 | C | SomeValue
Table R
101 | 1 | A | RValue
102 | 2 | A | RValue
The SQL should return 1
SELECT
[X].Id_X
FROM
[R]
INNER JOIN [X] ON
[R].Id_X = [X].Id_X
AND
[R].GroupId = [X].GroupId
WHERE
[X].GroupId IN ( 'A', 'B' )
AND
[R].RColumn = 'RValue'
If i understand correctly, your query should be
DECLARE #TableX AS TABLE
(
Id_X int, GroupId varchar(10), SomeColumn varchar(20)
)
INSERT INTO #TableX
VALUES
( 1, 'A', 'SomeValue'),
( 1, 'B', 'SomeValue'),
( 2, 'A', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'C', 'SomeValue')
DECLARE #TableR AS TABLE
(
ID_R int, Id_X int, GroupId varchar(10),RColumn varchar(10)
)
INSERT INTO #TableR
VALUES (101,1,'A','RValue'), (102,2,'A','RValue')
SELECT DISTINCT tr.Id_X
FROM #TableR tr
INNER JOIN #TableX tx ON tx.Id_X = tr.Id_X AND tx.GroupId = tr.GroupId
WHERE tr.RColumn = 'RValue'
AND NOT EXISTS ( SELECT 1 FROM #TableX tx2
WHERE tx2.Id_X = tx.Id_X
AND tx2.GroupId NOT IN ('A','B')
)
Demo link: http://rextester.com/EGLOT75874

Return Multiple Rows in One Cell

So I've looked around and seen the XML trick and the Variable trick, and neither really made enough sense to me to implement. What I have is a table with 4 Columns, The first is a unique identifier, the second is a relation to a different table, the third is varbinary(max), the last is a string. I want to combine columns three and four over column two. Is this possible?
Example of Data:
| FileId | UniqueI1 | BinaryData | FileName |
|---------+------------+--------------+----------|
| 1 | 1 | <byte> | asp.jpg |
| 2 | 1 | <byte> | asp1.jpg |
| 3 | 2 | <byte> | asp2.jpg |
| 4 | 2 | <byte> | asp3.jpg |
| 5 | 2 | <byte> | asp4.jpg |
Preferred Output:
| UniqueI1 | BinaryData | FileName |
|------------+------------------------------+------------------------------|
| 1 | <byte>, <byte> | asp.jpg, asp1.jpg |
| 2 | <byte>, <byte>, <byte> | asp2.jpg, asp3.jpg, asp4.jpg |
I appreciate any help you may be able to provide me.
Sounds like you're trying to group your data and aggregate the BinaryData and FileName columns by concatenating their values.
There are no built-in aggregates for concatenation in t-sql, but there are a couple of ways to reach the same results.
In my opinion, by far the easiest way is to write a custom aggregate in c# leveraging the CLR. But it can also be done using STUFF or XML. You should have a look at Does T-SQL have an aggregate function to concatenate strings?
Try this:
DECLARE #t TABLE
(
FileID INT ,
UniqueID INT ,
Data VARBINARY(100) ,
FileName VARCHAR(10)
)
INSERT INTO #t
VALUES ( 1, 1, 1, 'asp.jpg' ),
( 2, 1, 2, 'asp1.jpg' ),
( 3, 2, 3, 'asp2.jpg' ),
( 4, 2, 4, 'asp3.jpg' ),
( 5, 2, 5, 'asp4.jpg' )
SELECT UniqueID ,
MAX(ca.data) AS Data,
MAX(ca.name) AS Name
FROM #t t1
CROSS APPLY ( SELECT STUFF(
(SELECT ', ' + CONVERT(VARCHAR(MAX), t2.Data, 2)
FROM #t t2
WHERE t1.UniqueID = t2.UniqueID
ORDER BY FileID
FOR XML PATH('') ,
TYPE
).value('.', 'varchar(max)'), 1, 2, '') AS DATA ,
STUFF(
(SELECT ', ' + t2.FileName
FROM #t t2
WHERE t1.UniqueID = t2.UniqueID
ORDER BY FileID
FOR XML PATH('') ,
TYPE
).value('.', 'varchar(max)'), 1, 2, '') AS NAME
) ca
GROUP BY UniqueID
Output:
UniqueID Data Name
1 00000001, 00000002 asp.jpg, asp1.jpg
2 00000003, 00000004, 00000005 asp2.jpg, asp3.jpg, asp4.jpg
For pivoting:
WITH cte
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY UniqueID ORDER BY FileID ) AS rn
FROM #t
)
SELECT c.UniqueID ,
ca1.[1] AS Data1 ,
ca1.[2] AS Data2 ,
ca1.[3] AS Data3 ,
ca2.[1] AS File1 ,
ca2.[2] AS File2 ,
ca2.[3] AS File3
FROM cte c
CROSS APPLY ( SELECT *
FROM ( SELECT UniqueID ,
rn ,
Data
FROM cte ci
WHERE ci.UniqueID = c.UniqueID
) t PIVOT( MAX(Data) FOR rn IN ( [1], [2], [3] ) ) p
) ca1
CROSS APPLY ( SELECT *
FROM ( SELECT UniqueID ,
rn ,
FileName
FROM cte ci
WHERE ci.UniqueID = c.UniqueID
) t PIVOT( MAX(FileName) FOR rn IN ( [1], [2], [3] ) ) p
) ca2
GROUP BY c.UniqueID, ca1.[1], ca1.[2], ca1.[3], ca2.[1], ca2.[2], ca2.[3]
Output:
UniqueID Data1 Data2 Data3 File1 File2 File3
1 0x00000001 0x00000002 NULL asp.jpg asp1.jpg NULL
2 0x00000003 0x00000004 0x00000005 asp2.jpg asp3.jpg asp4.jpg
You can change this to dynamic query if you don't want to manually add additional files.