multiple INSERTs in single CTE pipeline - sql

I have an input table that looks like this:
intput(code, persons)
'ID1', 'Pete Sampras,Ricky Martin,Albert Einstein'
'ID2', 'Georges Mikael, Diego Maradona'
I want to produce 3 output tables:
ids_table(idcode, code)
1, 'ID1'
2, 'ID2'
persons(idperson, FirstName, LastName)
1, 'Pete', 'Sampras'
2, 'Ricky', 'Martin'
3, 'Albert', 'Einstein'
4, 'Georges', 'Mikael'
5, 'Diego', 'Maradona'
code_persons(idcode, idperson)
1, 1
1, 2
1, 3
2, 4
2, 5
I am using a pipeline of CTEs to do this. I was wondering if you could do multiple inserts in the same CTE pipeline. Something like:
WITH cte AS (
SELECT code, [value] AS person
FROM input
CROSS APPLY STRING_SPLIT(persons, ',')
), cte2 AS (
SELECT code, person, CHARINDEX(' ',person) AS splitIndex
FROM cte
), cte3 AS (
SELECT code, person, LEFT(person, splitIndex) AS FirstName,
SUBSTRING(person, splitIndex, 100) AS LastName
FROM cte2
), cte4 AS (
INSERT INTO ids_table(code)
SELECT DISTINCT code
FROM cte3
), cte5 AS (
INSERT INTO persons(FirstName, LastName)
SELECT FirstName, LastName
FROM cte3
), cte6 AS (
INSERT INTO code_persons(idcode, idpersons)
SELECT it.idcode, p.idperson
FROM cte3
JOIN persons p ON p.FirstName = cte3.FirstName AND p.LastName=cte3.LastName
JOIN ids_table it ON cte3.code=it.code
)
--Some code to trigger execution
One solution I can think of is to store the output of cte3 into a temporary table.
Is there a way to avoid this ?

Related

add row value to column in oracle

I have a table like this:
now;
I want map row value to column with SELECT statement.
result should be this:
You can do something like this.
Select * from (select id, person_id, f_device, report_date, originaldate)
pivot
(
count(*)
for person_id in (1, 2, 3, 4)
);
That is you need to aggregate and you will get columns that are in the select statement and for clause. This query will count rows for person_id in (1, 2, 3, 4) and group by the remaining columns internally.
Since your question is not clear if this is not the case please try to improve your question.
Update
WITH cte as ( select personid id, reportdate c_date, listagg(originaldate||' ') og FROM my_table GROUP BY personid, reportdate),
cte2 as (SELECT id, c_date, regexp_substr(og, '(.*?)( |$)', 1, 1 ) one,
regexp_substr(og, '(.*?)( |$)', 1, 2) two,
regexp_substr(og, '(.*?)( |$)', 1, 3 ) three,
regexp_substr(og, '(.*?)( |$)', 1, 4 ) four
from cte)
SELECT * FROM cte2;
If you want a distinct personid each row then reportdate needs to be aggregated
WITH cte as ( select personid id, max(reportdate ) c_date, listagg(originaldate||' ') WITHIN GROUP(ORDER BY personid ) og FROM my_table GROUP BY personid),
cte2 as (SELECT id, c_date, regexp_substr(og, '(.*?)( |$)', 1, 1 ) one,
regexp_substr(og, '(.*?)( |$)', 1, 2) two,
regexp_substr(og, '(.*?)( |$)', 1, 3 ) three,
regexp_substr(og, '(.*?)( |$)', 1, 4 ) four
from cte)
SELECT * FROM cte2;

SQL Dynamic Self Cross Join using TSql

I have a table that has data like this
Group Value
1 A
1 B
1 C
2 F
2 G
3 J
3 K
I want to join all members of one group to all members of each of the other groups into a single column like this:
AFJ
AFK
AGJ
AGK
BFJ
BFK
BGJ
BGK
CFJ
CFK
CGJ
CGK
There can be n number of groups and n number of values
Thank you
SQL does not offer many options for such a query. The one standard method is a recursive CTE. Other methods would involve recursive functions or stored procedures.
The following is an example of a recursive CTE that solves this problem:
with groups as (
select v.*
from (values (1, 'a'), (1, 'b'), (1, 'c'), (2, 'f'), (2, 'g'), (3, 'h'), (3, 'k')
) v(g, val)
),
cte as (
select 1 as groupid, val, 1 as lev
from groups
where g = 1
union all
select cte.groupid + 1, cte.val + g.val, lev + 1
from cte join
groups g
on g.g = cte.groupid + 1
)
select val
from (select cte.*, max(lev) over () as max_lev
from cte
) cte
where lev = max_lev
order by 1;
Some databases that support recursive CTEs don't use the recursive keyword.
Here is a db<>fiddle.
If you need to consider gaps between the groups, i.e. Group 1, then no 2, then 3.
with t(Grp, val) as (
select 1, 'A' from dual
union all
select 1, 'B' from dual
union all
select 1, 'C' from dual
union all
select 2, 'F' from dual
union all
select 2, 'G' from dual
union all
select 3, 'J' from dual
union all
select 3, 'K' from dual
)
, grpIndexes as (
SELECT ROWNUM indx
, grp
FROM (select distinct
grp
from t)
)
, Grps as (
select t.*
, grpIndexes.indx
from t
inner join grpIndexes
on grpIndexes.grp = t.grp
)
, rt(val, indx, lvl) as (
select val
, indx
, 0 as lvl
from Grps
where indx = (select min(indx) from Grps)
union all
select previous.val || this.val as val
, this.indx
, previous.lvl + 1 as lvl
from rt previous
, Grps this
where this.indx = (select min(indx) from Grps where indx > previous.indx)
and previous.indx <> (select max(indx) from Grps)
)
select val
from rt
where lvl = (select max(lvl) from rt)
order by val
;
Note that I renamed the columns because Group and Value are reserved words.

How to specify the order results with a SQL UNION?

I'm trying to run the following query:
select * from (select * from customquestionbank where questionid=6 or secondquestionid=6
union select * from customquestionbank where questionid=5 or secondquestionid=5
union select * from customquestionbank where questionid=10 or secondquestionid=10
union select * from customquestionbank where questionid=11 or secondquestionid=11) Tabled
Being new to this site I cannot post images yet, but here is what the result look like:
questionid -> 5,6 ,10,11
However, I want the result to be displayed in the same order as my select statements above. In other words, questionid=6 returned first, then 5, and so on.
You don't need all the unions, just make it like this:
SELECT DISTINCT *
FROM customquestionbank
WHERE questionid IN ( 6, 5, 10, 11 )
OR secondquestionid IN ( 6, 5, 10, 11 )
ORDER BY CASE
WHEN 6 IN ( questionid, secondquestionid ) THEN 0
WHEN 5 IN ( questionid, secondquestionid ) THEN 1
WHEN 10 IN ( questionid, secondquestionid ) THEN 2
WHEN 11 IN ( questionid, secondquestionid ) THEN 3
END
If your RDBMS supports the VALUES constructor
SELECT questionid, secondquestionid
FROM (VALUES(6,1),
(5, 2),
(10, 3),
(11, 4)) V(q, ord)
JOIN customquestionbank
ON q IN ( questionid, secondquestionid )
GROUP BY questionid, secondquestionid
ORDER BY MIN(ord)
if your RDBMS is mysql, I think you can try ORDER BY FIELD like this:
SELECT *
FROM customquestionbank
WHERE
questionid IN(6, 5, 10, 11)
OR secondquestionid IN(6, 5, 10, 11)
ORDER BY FIELD(questionid, 6) ASC
Remove the super select that you are using
Write the query as
Select * from query1 union
Select * from query2 union
Select * from query3;
This will bring your result as needed
Else try this
Select col1,col2 from( Select 1,q1.* from query1 q1 union
Select 2,q2.* from query2 q2 union
Select 3,q1.* from query3 q3)
select only the required columns in the super query
Replace Union with Union ALL
create table #t
(
id int
)
insert into #t(id)values(5)
insert into #t(id)values(2)
insert into #t(id)values(4)
insert into #t(id)values(3)
Select * from
(
Select * from #t where id = 5
uNION All
Select * from #t where id = 2
uNION All
Select * from #t where id = 4
uNION All
Select * from #t where id = 3
)K
DROP TABLE #t

SQL to get an ordinal index in selection for specific ID

I'm working with the SQL Server database. Say, I have a selection that returns an array of IDs. Can I find out (with SQL) the order of an ID equalled N in that array?
Here's what I mean. This is my actual selection:
SELECT [id] FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2
ON t1.[id]=t2.[itmid]
ORDER BY t2.[iid] ASC;
Say, it will return the following array of IDs:
13, 7, 34, 5, 2, 14
And, say I need to know what index is ID=5 in that array (the answer is 3, if 13 is index 0, 7 = index 1, 34 = index 2, 5 = becomes index 3.)
Try using ROW_NUMBER():
SELECT ROW_NUMBER() OVER (ORDER BY t2.[iid] ASC) -1 AS RowIndex
,[id]
FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2
ON t1.[id]=t2.[itmid]
ORDER BY t2.[iid] ASC;
More on it here http://msdn.microsoft.com/en-us/library/ms186734.aspx
Try this:
SELECT x.RowNum - 1
FROM
(
SELECT [id], ROW_NUMBER() OVER (ORDER BY t2.[iid]) as RowNum
FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2 ON t1.[id]=t2.[itmid]
) x
WHERE x.[id] = 5
Note that the -1 is because ROW_NUMBER() starts at 1 instead of 0 and you specifically mentioned a zero-indexed array.
Try this... Though I'm not sure on your table structure
Declare #Temp table
(
id int,
Name varchar(20)
)
Insert into #Temp
select 1, 'Bob'
union all
select 2, 'Mark'
union all
select 3, 'Shaun'
union all
select 4, 'Ryan'
union all
select 5, 'Steve'
union all
select 6, 'Bryan'
union all
select 7, 'Henry'
Declare #Temp2 table
(
iid int,
itmid int,
Name varchar(20)
)
Insert into #Temp2
select 1, 3, 'Thing'
union all
select 2, 2, 'This'
union all
select 3, 5, 'That'
union all
select 4, 1, 'They'
union all
select 5, 3, 'There'
union all
select 6, 5, 'Though'
union all
select 7, 6, 'Thought'
SELECT t1.[id], Row_Number() OVER (Order by t1.[id]) as RowNum
FROM #Temp t1
LEFT JOIN #Temp2 t2 ON t1.[id]=t2.[itmid]
ORDER BY t1.[id] ASC;

SQL preferred one-to-many relationship

Say I have a one-to-many relationship between clients and addresses.
Clients can have multiple addresses with different address types (eg. home, postal, work, company, future), and might have no address or multiple addresses of the same type (in which case I'm happy to take the 1st occurrence).
My data consists of clientid, address, and addresstypeid. The preferred order of the addresstypeid is 2,3,4,1: so if a client has an addresstypeid of 2 return that, if not return the record with 3, if not 4, if not 1 and if not null.
Is there an more elegant way to do this than something like this?
declare #addresses table
(
clientid int,
address varchar(10),
addresstypeid int
)
--2,3,4,1
insert into #addresses (clientid, address, addresstypeid)
select 1, '1a', 1 union all
select 1, '1b', 2 union all
select 1, '1c', 3 union all
select 1, '1d', 4 union all
select 2, '2a', 2 union all
select 2, '2b', 3 union all
select 2, '2c', 4 union all
select 2, '2a', 1 union all
select 3, '3a', 3 union all
select 3, '3b', 4 union all
select 3, '3c', 1 union all
select 4, '4a', 3 union all
select 4, '4b', 4 union all
select 5, '5a', 4 union all
select 6, '6a', 4 union all
select 6, '6b', 1 union all
select 7, '7a', 1 union all
select 7, '7b', 4
declare #ranktable table
(
addresstypeid int,
rank int
)
insert into #ranktable(addresstypeid, rank)
select 2, 1 union all
select 3, 2 union all
select 4, 3 union all
select 1, 4
select
e.address,
e.clientid,
e.addresstypeid
from
#addresses e
inner join #ranktable r on
e.addresstypeid = r.addresstypeid
inner join (select
em.clientid,
min(rank) minrank
from #addresses em
inner join #ranktable ranks on
em.addresstypeid = ranks.addresstypeid
group by
clientid) clientranks on
e.clientid = clientranks.clientid and
r.rank = clientranks.minrank
The two tables are fine, though you should consider indexes when you make them permanent :)
In terms of your final query, I'd change it slightly...
WITH
sorted_data
AS
(
SELECT
[a].*,
ROW_NUMBER() OVER (PARTITION BY [a].clientid ORDER BY [r].rank) AS sequence_id
FROM
#addresses AS [a]
INNER JOIN
#ranktable AS [r]
ON a.addresstypeid = r.addresstypeid
)
SELECT
*
FROM
sorted_data
WHERE
sequence_id = 1