Unique coverage between a pair of values in SQL - sql

I want to get a distinct list of Item_IDs per vendor pair, e.g. (A,B), (A,C), (B,C)
I have a table like this:
Item_ID | vendor
123 A
123 B
133 B
456 C
I want to list ALL of the distinct Item_IDs between each pair of vendors, like below. Essentially, I want to know all of the Item_IDs which are "covered" (or present) between a pair of vendors.
Vendor1 | Vendor 2 | Item_ID
A B 123
A B 133
A C 123
A C 456
B C 123
B C 133
B C 456
I have tried using a self join, to get 2 vendors side by side, but struggling on how to list out the Item_IDs per pair.
Here is what I have so far:
select
distinct a.vendor as vendor1, b.vendor as vendor2
from table a
join table b on a.vendor != b.vendor
and a.vendor >= b.vendor -- to remove duplicate combinations

The following query works in SQL Server 2019 but I don't have the chance of testing it in Azure.
You can do:
with
v as (select distinct vendor as vendor from t)
select
a.vendor as vendor1,
b.vendor as vendor2,
x.item_id
from v a
join v b on b.vendor > a.vendor
cross apply (
select distinct item_id
from t
where t.vendor = a.vendor or t.vendor = b.vendor
) x
order by a.vendor, b.vendor
Result:
vendor1 vendor2 item_id
-------- -------- -------
A B 123
A B 133
A C 123
A C 456
B C 123
B C 133
B C 456
See running example at db<>fiddle.

Related

SQL query to find out other products that a customer purchased with a specific product

Below is the query that displays the customers who have used the key "3500". But there are other keys in the table.
I am trying to find out what other keys the below customers have used (in addition to 3500).
Would be great for any suggestions !!
SELECT distinct(b.id2)
FROM tab1 as a, tab2 as c, tab3 as b
WHERE a.id1 = c.id1 and c.id2 = b.id2
group by b.id2
having count
(
case
when key in (3500)
then 1
end
) > 0
id2 key
123 3500
123 3501
123 4100
234 3500
234 1234
234 4100
312 3500
312 4100
Result: (The above list is only the customers who have used "3500" in combination with other product keys)
key(other than 3500) count
3501 1
4100 3
1234 1
Without knowing the structure of the underlying tables, it's not possible to give the most specifically applicable answer, but here's one that should work:
with t as (
SELECT *
FROM
tab1 as a
INNER JOIN tab2 as c ON c.id1=a.id1
INNER JOIN tab3 as b ON b.id2=c.id2
),
custkeys as (
select distinct id2, key
from t
),
cust3500 as (
select distinct id2
from custkeys
where key = 3500
)
select
key,
count(*) as customers
from
custkeys as k
inner join cust3500 as c on c.id2=k.id2
where
key <> 3500;
The custkeys CTE might be unnecessary, or might be replaced with something simpler, and the cust3500 CTE might also be altered, depending on the actual structure of your tables.

combine three tables in oracle sql

I'm having these three tables
Table A:
code aname
----------- ----------
1 A
2 B
3 C
Table B:
code bname
----------- ----------
1 aaa
1 bbb
2 ccc
2 ddd
Table C
code cname
----------- ----------
1 xxx
1 yyy
1 zzz
2 www
How can I achieve the output like this using single query ?
code aname bname cname
----------- ---------- ---------- ----------
1 A aaa xxx
1 A bbb yyy
1 A NULL zzz
2 B ccc www
2 B ddd NULL
3 C NULL NULL
Any suggestions ?
Thanks
It looks like you want "lists" to be vertical for tables B and C. This is doable, by using row_number(). However, the trick is getting the third row in where there are no matches.
Here is one method. It uses a full outer join to combine the b and c names together. It then uses left join to bring in the a records.
select a.code, a.name, bc.bname, bc.cname
from a left join
(select coalesce(b.code, c.code) as code, bname, cname
from (select code, bname, NULL as cname,
row_number() over (partition by code order by code) as seqnum
from b
) b full outer join
(select code, NULL as bname, cname,
row_number() over (partition by code order by code) as seqnum
from c
) c
on b.code = c.code and b.seqnum = c.seqnum
) bc
on bc.code = a.code;

Left join with a select dependent on rows value

I have a table, tableA
Foo bar matcher
a b 456
c d 123
e f 789
…
And I have another very large table, tableB
Count matcher
1 123
2 456
2 123
...
From tableB I want to find details for a specific matcher
count matcher
1 123
2 123
But I just want to use the row with the maximum count
count matcher
2 123
Then I wish to left join tableB onto tableA
foo bar matcher count
a b 456 10
c d 123 2
e f 789 5
...
How do I do this?
select a.Foo, a.bar, b.matcher, max(b.Count) Count
from tableB b
left join tableA a
on a.matcher = b.matcher
group by a.Foo, a.bar, b.matcher

Get the max value of a column from set of rows

I have a table like this
Table A:
Id Count
1 4
1 16
1 8
2 10
2 15
3 18
etc
Table B:
1 sample1.file
2 sample2.file
3 sample3.file
TABLE C:
Count fileNumber
16 1234
4 2345
15 3456
18 4567
and so on...
What I want is this
1 sample1.file 1234
2 sample2.file 3456
3 sample3.file 4567
To get the max value from table A I used
Select MAX (Count) from A where Id='1'
This works well but my problem is when combining data with another table.
When I join Table B and Table A, I need to get the MAX for all Ids and in my query I dont know what Id is.
This is my query
SELECT B.*,C.*
JOIN A on A.Id = B.ID
JOIN C on A.id = B.ID
WHERE (SELECT MAX(COUNT)
FROM A
WHERE Id = <what goes here????>)
To summarise, what I want is Values from Table B, FileNumber from Table c (where the count is Max for ID from table A).
UPDATE: COrrecting table C above. Looks like I need Table A.
I think this is the query you're looking for:
select b.*, c.filenumber from b
join (
select id, max(count) as count from a
group by id
) as NewA on b.id = NewA.id
join c on NewA.count = c.count
However, you should take into account that I don't get why for id=1 in tableA you choose the 16 to match against table C (which is the max) and for id=2 in tableA you choose the 10 to match against table C (which is the min). I assumed you meant the max in both cases.
Edit:
I see you've updated tableA data. The query results in this, given the previous data:
+----+---------------+------------+
| ID | FILENAME | FILENUMBER |
+----+---------------+------------+
| 1 | sample1.file | 1234 |
| 2 | sample2.file | 3456 |
| 3 | sample3.file | 4567 |
+----+---------------+------------+
Here is a working example
Using Mosty’s working example (renaming the keyword count to cnt for a column name), this is another approach:
with abc as (
select
a.id,
a.cnt,
rank() over (
partition by a.id
order by cnt desc
) as rk,
b.filename
from a join b on a.id = b.id
)
select
abc.id, abc.filename, c.filenumber
from abc join c
on c.cnt = abc.cnt
where rk = 1;
select
PreMax.ID,
B.FileName,
C2.FileNumber
from
( select C.id, max( C.count ) maxPerID
from TableC C
group by C.ID
order by C.ID ) PreMax
JOIN TableC C2
on PreMax.ID = C2.ID
AND PreMax.maxPerID = C2.Count
JOIN TableB B
on PreMax.ID = B.ID

Sql Inner Join first record only if Exists Take Next one

This one is support hard for me. I can do inner join with first result only, but if exist I want take 2nd result.
THIS IS MY TABLE A
ID NAME VALUE
1 A 123
2 B 456
3 C 789
4 A 456
TABLE B
BID BNAME BVALUE
1 A ABC
2 A CDE
3 B 845
4 C 1234
MY SELECT SQL:
SELECT * FROM A
CROSS APPLY (
SELECT TOP 1 *
FROM B
WHERE A.Name = B.BName
) BB
It return
1 A 123 1 A ABC
2 B 456 3 B 845
3 C 789 4 C 1234
4 A 456 1 A ABC
Please help, I want this result:
1 A 123 1 A ABC
2 B 456 3 B 845
3 C 789 4 C 1234
4 A 456 2 A CDE
I accept tmp table and any kind of query :(
Following clarification in the comments that both tables will always have matching rows.
WITH A
AS (SELECT *,
ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY ID) AS RN
FROM TableA),
B
AS (SELECT *,
ROW_NUMBER() OVER (PARTITION BY BNAME ORDER BY BID) AS RN
FROM TableB)
SELECT A.ID,
A.NAME,
A.VALUE,
B.BID,
B.BNAME,
B.BVALUE
FROM A
JOIN B
ON A.NAME = B.BNAME
AND A.RN = B.RN