Join two child records next to each other in one row in PL Sql [duplicate] - sql

This question already has an answer here:
Join/Pivot items with EAV table
(1 answer)
Closed 1 year ago.
I have 2 tables,
select * from Srn_Table1;
ID
CUSTOMERNAME
1
TEST CUSTOMER
select * from Srn_Table2;
ID
ADDRESS
ADDRESSID
1
palakkad
1
1
thrissur
2
I need the Result as
ID
ADDRESS
ADDRESSID
ADDRESS
ADDRESSID
1
palakkad
1
thrissur
2
ID is common.
How can I join the 2 tables to get the result..?

I would add a row number to the second table, so that you can join that table once for when that row number is 1, and a second time for when it is 2:
with ext as (
select Srn_Tablea2.*,
row_number() over (partition by id order by addressid) rn
from Srn_Tablea2
)
select a.id, b.address, b.addressid, c.address, c.addressid
from Srn_Tablea1 a
left join ext b on a.id = b.id and b.rn = 1
left join ext c on a.id = c.id and c.rn = 2;
Of course, if you have cases where you have 3 or more addresses for the same id, you'll have to create more joins, and produce more columns. But the principle remains the same.

Related

Return only one row based on search

Query
select
a.id,
a.ba,
b.status,
b.custid
from balist as a
inner join customer as b
on a.ba = b.ba
I have a table "balist" that has a list of (ba) and i inner join table "customer" on (ba) and right now by output is like the following
id
ba
status
custid
1
ba-1234455
A
123-321-123-321a
2
ba-1234455
I
123-321-123-321a
3
ba-1234457
A
123-321-123-321b
4
ba-1234458
A
123-321-123-321c
5
ba-1234459
I
123-321-123-321d
and I want to return all A and I status but remove the row that has status I that also have a A status. Like the following.
I have a table customer like the following
id
ba
status
custid
1
ba-1234455
A
123-321-123-321a
3
ba-1234457
A
123-321-123-321b
4
ba-1234458
A
123-321-123-321c
5
ba-1234459
I
123-321-123-321d
You could use a row_number() to filter your resulting rows eg
SELECT
id,ba,status,custid
FROM (
SELECT
a.id,
a.ba,
b.status,
b.custid,
ROW_NUMBER() OVER (
PARTITION BY a.ba
ORDER BY b.status ASC
) as rn
FROM
balist as a
INNER JOIN
customer as b ON a.ba = b.ba
)
WHERE rn=1
Let me know if this works for you.

Combine rows from Mulitple tables into single table

I have one parent table Products with multiple child tables -Hoses,Steeltubes,ElectricCables,FiberOptics.
ProductId -Primary key field in Product table
ProductId- ForeignKey field in Hoses,Steeltubes,ElectricCables,FiberOptics.
Product table has 1 to many relationship with Child tables
I want to combine result of all tables .
For eg - Product P1 has PK field ProductId which is used in all child tables as FK.
If Hoses table has 4 record with ProductId 50 and Steeltubes table has 2 records with ProductId 50 when I perform left join then left join is doing cartesian product of records showing 8 record as result But it should be 4 records .
;with HOSESTEELCTE
as
(
select '' as ModeType, '' as FiberOpticQty , '' as NumberFibers, '' as FiberLength, '' as CableType , '' as Conductorsize , '' as Voltage,'' as ElecticCableLength , s.TubeMaterial , s.TubeQty, s.TubeID , s.WallThickness , s.DWP ,s.Length as SteelLength , h.HoseSeries, h.HoseLength ,h.ProductId
from Hoses h
left join
(
--'' as HoseSeries,'' as HoseLength ,
select TubeMaterial , TubeQty, TubeID , WallThickness , DWP , Length,ProductId from SteelTubes
) s on (s.ProductId = h.ProductId)
) select * from HOSESTEELCTE
Assuming there are no relationships between child tables and you simply want a list of all child entities which make up a product you could generate a cte which has a number of rows which are equal to the largest number of entries across all the child tables for a product. In the example below I have used a dates table to simplify the example.
so for this data
create table products(pid int);
insert into products values
(1),(2);
create table hoses (pid int,descr varchar(2));
insert into hoses values (1,'h1'),(1,'h2'),(1,'h3'),(1,'h4');
create table steeltubes (pid int,descr varchar(2));
insert into steeltubes values (1,'t1'),(1,'t2');
create table electriccables(pid int,descr varchar(2));
truncate table electriccables
insert into electriccables values (1,'e1'),(1,'e2'),(1,'e3'),(2,'e1');
this cte
;with cte as
(select row_number() over(partition by p.pid order by datekey) rn, p.pid
from dimdate, products p
where datekey < 20050105)
select * from cte
create a cartesian join (one of the rare ocassions where an implicit join helps) pid to rn
result
rn pid
-------------------- -----------
1 1
2 1
3 1
4 1
1 2
2 2
3 2
4 2
And if we add the child tables
;with cte as
(select row_number() over(partition by p.pid order by datekey) rn, p.pid
from dimdate, products p
where datekey < 20050106)
select c.pid,h.descr hoses,s.descr steeltubes,e.descr electriccables from cte c
left join (select h.*, row_number() over(order by h.pid) rn from hoses h) h on h.rn = c.rn and h.pid = c.pid
left join (select s.*, row_number() over(order by s.pid) rn from steeltubes s) s on s.rn = c.rn and s.pid = c.pid
left join (select e.*, row_number() over(order by e.pid) rn from electriccables e) e on e.rn = c.rn and e.pid = c.pid
where h.rn is not null or s.rn is not null or e.rn is not null
order by c.pid,c.rn
we get this
pid hoses steeltubes electriccables
----------- ----- ---------- --------------
1 h1 t1 e1
1 h2 t2 e2
1 h3 NULL e3
1 h4 NULL NULL
2 NULL NULL e1
In fact, the result having 8 rows can be expected to be the result, since your four records are joined with the first record in the other table and then your four records are joined with the second record of the other table, making it 4 + 4 = 8.
The very fact that you expect 4 records to be in the result instead of 8 shows that you want to use some kind of grouping. You can group your inner query issued for SteelTubes by ProductId, but then you will need to use aggregate functions for the other columns. Since you have only explained the structure of the desired output, but not the semantics, I am not able with my current knowledge about your problem to determine what aggregations you need.
Once you find out the answer for the first table, you will be able to easily add the other tables into the selection as well, but in case of large data you might get some scaling problems, so you might want to have a table where you store these groups, maintain it when something changes and use it for these selections.

SQL joining two tables with common row

I have 2 tables in sybase
Account_table
Id account_code
1 A
2 B
3 C
Associate_table
id account_code
1 A
1 B
1 C
2 A
2 B
3 A
3 C
I have this sql query
SELECT * FROM account_table account, associate_table assoc
WHERE account.account_code = assoc.account_code
This query will return 7 rows. What I want is to return the rows from associate_table that is only common to the 3 accounts like this:
account id account_code Assoc Id
1 A 1
2 B 1
3 C 1
Can anyone help what kind of join should I do?
SELECT b.id account_id,a.code account_code,a.id assoc_id
FROM associate a,
account b
WHERE a.code = b.code
AND a.id IN (SELECT a.id
FROM associate a,
account b
WHERE a.code = b.code
GROUP BY a.id
HAVING Count(*) = (SELECT Count(*)
FROM account));
NOTE: this query works only if you have unique values in Id and account_code columns in account table. And also, your associate_table should contain unique combination of (id, account,code). i.e., associate table should not contain (1,A) or any pair twice.
Try this
SELECT AC.ID,AC.account_code,ASS.ID
FROM account_table AC INNER JOIN associate_table AS ASS ON AC.account_code = ASS.account_code
OK so far answer is accepted I'll post simpler one:
SELECT *
FROM account_table AS account,
associate_table AS assoc
WHERE account.account_code = assoc.account_code
HAVING (
SELECT
COUNT(*)
FROM associate_table assoc_2
WHERE assoc_2.id = assoc.id
) = 3
here 3 is the number of codes account table has, if it's gonna be dynamic (changing over time),
you can use (SELECT COUNT(*) FROM account_table) instead of exact number. Also I'm sure it will be cached by database engine, so requires less resources

Linking and counting records with one or multiple criteria

I am attempting to link together some records to identify sibling groups. The way we can do this is to identify clients who share the same parents.
A sample of the SQL follows:
SELECT
A.ClientID,
B.ParentID
FROM A
LEFT JOIN B ON B.ClientID to A.ClientID
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
Which would return data in the following format:
Client ID Parent ID
1 A
1 B
2 C
2 D
3 C
3 E
4 C
4 D
How I would like it to appear follows:
Client ID No. of Siblings
1 0
2 2
3 2
4 2
Hopefully the table shows that child 1 has 0 siblings (shares no parents with 2,3,4), child 2 has 2 siblings (3 and 4), child 3 has 2 siblings (2 and 4) and child 4 has 2 siblings (2,3). It seems like it should be simple enough to achieve this but I'm really struggling at the minute to think how! I think it's made slightly more confusing because a child may share only one parent with another child to be considered as a sibling.
Hopefully this is clear, thanks.
Edit: Just to make it clear, the relationship is identified by a child sharing a parent ID with another child (the ids are unique, but I provided generic ones for this example). So as Child 2, 3 and 4 all have a parent with an id of C they are considered siblings.
You may try this query, it displays to me the desired output.
with c_data as (
select a.clientid, b.parentid, count(a.clientid) over (partition by parentid order by parentid) as c_parents
FROM A
LEFT JOIN B ON (B.ClientID = A.ClientID)
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
)
select clientid as "Client ID", max(c_parents) -1 as "No of Siblings"
from c_data
group by clientid;
Example:
SQL> with c_data as (
2 select a.clientid, b.parentid, count(a.clientid) over (partition by parentid order by parentid) as c_parents
3 FROM A
4 LEFT JOIN B ON (B.ClientID = A.ClientID)
5 AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
6 )
7 select clientid as "Client ID", max(c_parents) -1 as "No of Siblings"
8 from c_data
9 group by clientid;
Client ID No of Siblings
---------- --------------
1 0
2 2
4 2
3 2
Transcurrido: 00:00:00.03
SQL>
With the analytic function we count all client id's partitioned by the parentid related of the current tupple to count all clients that have in common the same parent.
After in the projection, we get the max number of parent in common for each client and substracts 1, the client itself.
Hope this helps!
Regards!
This is rather complicated. If you can assume exactly two parents for each child, then you can do something like:
select c.*, count(*) over (partition by min_parent, max_parent) - 1 as NumSiblings
from (SELECT A.ClientID, min(B.ParentID) as min_parent, max(b.parentid) as max_parent
FROM A LEFT JOIN
B
ON B.ClientID to A.ClientID AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
group by a.clientid
) c
What this does is calculate the two parents for each client. It then uses the windows function to count the number of clients that have exactly the same parents. The "-1" is because all children are counted, and we don't want to count the current child.
If you can have more than two parents, then the query is more complicated.
If you want only one parent being shared (rather than two), then you can handle using a self join:
with cp as (SELECT A.ClientID, B.ParentID
FROM A LEFT JOIN
B
ON B.ClientID to A.ClientID AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
)
select cp.client_id, count(distinct cp1.client_id) as NumSiblings
from cp left outer join
cp cp1
on cp.parent_id = cp1.parent_id and cp.client_id <> cp1.client_id
group by cp.client_id
Well, I don't understand how the table and relation are made, but you could do something like that :
SELECT ClientID, sum(NumberOfSibling) as NumberOfSibling
from(
SELECT A.ClientID, (select count(ClientID)
from A2
LEFT JOIN B2 ON B2.ClientID to A2.ClientID
AND B2.REL_END is NULL AND B2.REL_CODE = 'PAR'
where B2.ParentID = B.ParentID) as NumberOfSibling
FROM A
LEFT JOIN B ON B.ClientID to A.ClientID
AND B.REL_END is NULL AND B.REL_CODE = 'PAR'
) GROUP BY ClientID
Explanation : I took your request and modify the second result to return the sum of siblings wich possess the same Parent (that's the select count(*) ..., that's also the part I'm not sure about the conditions). And put this in another to group by ClientID to have the sum of the 2 parents.

Joining 3 Tables Using Newest Rows

I have 3 tables in my database: children, families, statuslog
Every time a child is checked in or out of the database, it is updated in the statuslog. I've done this a long time ago, but I can't seem to figure out how to do it anymore. I want to create a new view that joins all 3 tables, but I only want the newest entry from statuslog (by using the highest id).
For example, statuslog looks like this:
childID researcher status id
1 Dr. A out 1
1 Dr. A in 2
1 Dr. B out 3
1 Dr. B in 4
2 Dr. C out 5
2 Dr. C in 6
3 Dr. B out 7
3 Dr. B in 8
This is what I want to do:
SELECT *
FROM children, families, statuslog
WHERE children.familyID = families.familyID AND children.childID = statuslog.childID
Obviously, this will return the children+families tuples coupled with every single log entry, but I can't remember how to only combine it with the newest log entry.
Any help would be appreciated!
Aggregate query with max(id) retrieves last ID given a childID. This is then joined to statuslog to retrieve other columns.
SELECT *
FROM children
INNER JOIN families
ON children.familyID = families.familyID
INNER JOIN
(
SELECT childID, researcher, status
FROM statuslog
INNER JOIN
(
SELECT childID, max(ID) ID
FROM statuslog
GROUP BY childID
) lastSL
ON statuslog.childID = lastSL.childid
AND statuslog.ID = lastSL.ID
) sl
ON children.childID = sl.childID
This seems to be the typical greatest-n-per-group in which the higher id is interpreted as the newest. This query should do the trick:
select * from (
select s1.* from statusLog s1
left join statusLog s2
on s1.childId = s2.childId and s1.id < s2.id
where s2.id is null
) final
join children c on c.childId = final.childId
join families f on f.familyId = c.familyId
Correct any syntactical errors.