SQL hierarchy - getting newest grandchild - sql

I have a H2 database with a LEFT JOIN query on Bird which returns all birds along with the newest Health_Check.Catch_Date. I want to extend this query and include in the results newest PIT.ID, PIT.CODE and TRANSMITTER.IDc, TRANSMITTER.CHANNEL which are related to Health_Check.
Note: not every Health_Check has a relationship to a Transmitter or PIT.
A few notes about how the data is structured.
Most birds have several health checks;
Not every health check has a associated PIT or TRANSMITTER;
The newest Health_Check for a bird may not contain a PIT or TRANSMITTER;
Most birds have one PIT;
Most birds have a few Transmitters;
The date the Transmitter or PIT was attached is based on the related Health_Check.Catch_Date;
Most of my queries will require returning a Bird with the newest Health Check, PIT and Transmitter.
The SQL to get the newest HEALTH_CHECK is:
SELECT b.NAME, b.ID as birdId, hc1.CATCH_DATE, hc1.id as healthCheckId
FROM BIRD b
LEFT OUTER JOIN
( HEALTH_CHECK hc1
INNER JOIN
(
SELECT BIRD_ID, MAX(CATCH_DATE) AS MAX_DATE
FROM HEALTH_CHECK
GROUP BY BIRD_ID
) hc2
ON hc2.BIRD_ID = hc1.BIRD_ID AND
hc2.MAX_DATE = hc1.CATCH_DATE
)
ON hc1.BIRD_ID = b.ID;
Question: how can I also return in the results the most recent PIT and TRANSMITTER?
NB: The newest PIT and TRANS is often not on the newest HealthCheck. I am open to a schema change/adding a second relationship but I must be able to determine which HealthCheck a PIT and TRANS was attached in.
As an example results would look like:
BIRD.ID | BIRD.NAME | NEWEST HEALTH CHCECK | PIT.ID | PIT.CODE | TRANS.ID | TRANS.CHNL |
---------|-------------|----------------------|--------|----------|----------|------------|
1 | Bob | 2022-03-01 | AB001 | 3 | 2 | 40 |
2 | Jim | NULL | NULL | NULL | NULL | NULL |
3 | Jane | 2022-01-02 | DC123 | 2 | 3 | 50 |

One solution can be, to get the highest ID from every TRANs ANd PIT, i thinl that is the only ORDER that ic could find
Row_number would give every row for a specific chekc id, and the nimber 1 should be last one(ORDER BY ID DESC), it counts for every channel a new
SELECT b.NAME, b.ID as birdId, hc1.CATCH_DATE, hc1.id as healthCheckId
,PIT.ID , PIT.CODE,TRANS.ID , TRANS.CHANNEL
FROM BIRD b
LEFT OUTER JOIN
( HEALTH_CHECK hc1
INNER JOIN
(
SELECT BIRD_ID, MAX(CATCH_DATE) AS MAX_DATE
FROM HEALTH_CHECK
GROUP BY BIRD_ID
) hc2
ON hc2.BIRD_ID = hc1.BIRD_ID AND
hc2.MAX_DATE = hc1.CATCH_DATE
)
ON hc1.BIRD_ID = b.ID
LEFT JOIN
(SELECT ID, CODE,HEALTH_CHECK_ID
FROM
(SELECT ID , CODE,HEALTH_CHECK_ID
, ROW_NUMBER() OVER( PARTITION BY HEALTH_CHECK_ID ORDER BY ID DESC) rn FROM PIT) Pt
WHERE rn = 1) PIT ON PIT.HEALTH_CHeCK_ID = hc1.ID
LEFT JOIN
(SELECT ID, CHANNEL,HEALTH_CHECK_ID
FROM
(SELECT ID , CHANNEL,HEALTH_CHECK_ID
, ROW_NUMBER() OVER( PARTITION BY HEALTH_CHECK_ID ORDER BY ID DESC) rn FROM PIT) tr
WHERE rn = 1) TRANS ON TRANS.HEALTH_CHECK_ID = hc1.ID;
If there is a 1:1 relaionship you need only to join them,
SELECT b.NAME, b.ID as birdId, hc1.CATCH_DATE, hc1.id as healthCheckId
,PIT.ID , PIT.CODE,TRANS.ID , TRANS.CHANNEL
FROM BIRD b
LEFT OUTER JOIN
( HEALTH_CHECK hc1
INNER JOIN
(
SELECT BIRD_ID, MAX(CATCH_DATE) AS MAX_DATE
FROM HEALTH_CHECK
GROUP BY BIRD_ID
) hc2
ON hc2.BIRD_ID = hc1.BIRD_ID AND
hc2.MAX_DATE = hc1.CATCH_DATE
)
ON hc1.BIRD_ID = b.ID
LEFT JOIN
PIT ON PIT.HEALTH_CHeCK_ID = hc1.ID
LEFT JOIN
TRANS ON TRANS.HEALTH_CHECK_ID = hc1.ID;
if you need another heahc_ckes tarns and Pit that are not in the latest healthcheck, you need to add to the sunselect of the first query all healch ckes of that bird
SELECT b.NAME, b.ID as birdId, hc1.CATCH_DATE, hc1.id as healthCheckId
,PIT.ID , PIT.CODE,TRANS.ID , TRANS.CHANNEL
FROM BIRD b
LEFT OUTER JOIN
( HEALTH_CHECK hc1
INNER JOIN
(
SELECT BIRD_ID, MAX(CATCH_DATE) AS MAX_DATE
FROM HEALTH_CHECK
GROUP BY BIRD_ID
) hc2
ON hc2.BIRD_ID = hc1.BIRD_ID AND
hc2.MAX_DATE = hc1.CATCH_DATE
)
ON hc1.BIRD_ID = b.ID
LEFT JOIN
(SELECT ID, CODE,HEALTH_CHECK_ID,BIRD_ID
FROM
(SELECT ID , CODE,hc3.BIRD_ID,HEALTH_CHECK_ID
, ROW_NUMBER() OVER( PARTITION BY HEALTH_CHECK_ID ORDER BY hc3.CATCH_DATE DESC) rn FROM HEALTH_CHECK hc3 JOIN PIT ON hc3.ID = PIT.HEALTH_CHeCK_ID
WHERE hc3.BIRD_ID = hc1.BIRD_ID) Pt
WHERE rn = 1) PIT ON PIT.BIRD_ID = hc1.BIRD_ID
LEFT JOIN
(SELECT ID, CHANNEL,HEALTH_CHECK_ID,BIRD_ID
FROM
(SELECT ID , CHANNEL,HEALTH_CHECK_ID,BIRD_ID
, ROW_NUMBER() OVER( PARTITION BY HEALTH_CHECK_ID ORDER BY hc4.CATCH_DATE DESC) rn FROM HEALTH_CHECK hc4 JOIN PIT ON hc4.ID = PIT.HEALTH_CHeCK_ID
WHERE hc4.BIRD_ID = hc1.BIRD_ID) tr
WHERE rn = 1) TRANS ON TRANS.BIRD_ID = hc1.BIRD_ID;

For each bird (left join) to return a result set containing:
Newest health check (by date)
Recursively getting newest PIT (determined by date from related health check)
Recursively getting newest Trans (determined by date from related health check)
Noting that the PIT and Trans are often not on the newest Health Check can be done by:
SELECT *
FROM BIRD AS b
LEFT JOIN (
SELECT *
FROM (
SELECT id AS hc_id, bird_id, catch_date
, ROW_NUMBER() OVER (PARTITION BY bird_id ORDER BY catch_date DESC) AS n
FROM HEALTH_CHECK
) AS cte1a
WHERE n = 1
) AS cte1
ON cte1.bird_id = b.id
LEFT JOIN (
SELECT *
FROM (
SELECT p.id AS pit_id
, hc.bird_id
, p.Health_Check_Id
, p.code
, hc.catch_date as catchdate
, RANK() OVER (PARTITION BY hc.bird_id ORDER BY catch_date DESC) AS n
FROM PIT AS p
JOIN HEALTH_CHECK AS hc
ON p.Health_Check_Id = hc.id
) AS cte2a
WHERE n = 1
) AS cte2
ON cte2.bird_id = b.id
LEFT JOIN (
SELECT *
FROM (
SELECT t.id AS transmitter_id
, hc.bird_id
, t.Health_Check_Id
, t.channel
, hc.catch_date as catchdate
, RANK() OVER (PARTITION BY hc.bird_id ORDER BY catch_date DESC) AS n
FROM TRANSMITTER AS t
JOIN HEALTH_CHECK AS hc
ON t.Health_Check_Id = hc.id
) AS cte3a
WHERE n = 1
A fiddle is here.

Related

Need Full Outer Join without having Cross Join

Need to join two table without having cross join between them.
The join condition need to be made on Tabl.month = Tab2.month
Input
Table1 Table2
Month ID Month ID
1 a 1 a
1 b 1 b
1 c 2 g
2 d 3 i
2 e 3 j
3 f 3 k
Output:
Month_Tab1 ID_Tab1 Month_Tab2 ID_Tab2
1 a 1 a
1 b 1 b
1 c Null Null
2 d 2 g
2 e Null Null
3 f 3 i
Null Null 3 j
Null Null 3 k
The above o/p is required, without cross join, have tried full outer but cross join is happening as the ID is duplicate in both Tables. Left/Right join also cannt be applicable as either of the table might have larger set of ID's.
You want a full join, but with row_number() to identify the matches:
select t1.month month_tab1, t1.id id_tab1, t2.month month_tab2, t2.id id_tab2
from (
select t.*, row_number() over(partition by month order by id) rn from table1 t
) t1
full join (
select t.*, row_number() over(partition by month order by id) rn from table2 t) t2
on t2.month = t1.month and t2.rn = t1.rn
You can use a full outer join:
select
a.month,
a.id,
b.month,
b.id
from (
select month, id,
row_number() over(partition by month order by id) as n
from table1
) a
full outer join (
select month, id,
row_number() over(partition by month order by id) as n
from table2
) b on b.month = a.month and b.n = a.n
order by coalesce(a.month, b.month), coalesce(a.n, b.n)

Query for Retrieving

I need to build a query which should give me below mentioned output.
My table structure Required output
Depth | Name Level A | Level B | Level C
1 |A A | B | C
2 |B A1 | B1 | C1
3 |C A1 | B1 | C2
1 |A1 A1 | B2 | C3
2 |B1 A1 | B2 | C4
3 |C1
3 |C2
2 |B2
3 |C3
3 |C4
Thanks in advance
Given your particular data, you can come close with:
select a.name as levela, b.name as levelb, c.name as levelc
from (select name, row_number() over (order by id) as seqnum from table where depth = 1
) a full outer join
(select name, row_number() over (order by id) as seqnum from table where depth = 2
) b full outer join
on b.seqnum = a.seqnum
(select name, row_number() over (order by id) as seqnum from table where depth = 3
) c
on c.seqnum = coalesce(a.seqnum, b.seqnum);
This inserts NULLs instead of repeating the final values for the three columns. If you want the final values, this should work:
select coalesce(a.name, maxes.a) as levela,
coalesce(b.name, maxes.b) as levelb,
coalesce(c.name, maxes.c) as levelc
from (select name, row_number() over (order by id) as seqnum from table where depth = 1
) a full outer join
(select name, row_number() over (order by id) as seqnum from table where depth = 2
) b full outer join
on b.seqnum = a.seqnum
(select name, row_number() over (order by id) as seqnum from table where depth = 3
) c
on c.seqnum = coalesce(a.seqnum, b.seqnum) cross join
(select max(case when depth = 1 and id = maxid then name end) as max_a,
max(case when depth = 2 and id = maxid then name end) as max_b,
max(case when depth = 3 and id = maxid then name end) as max_c
from (select t.*,
max(id) over (partition by depth) as maxid
from t
) t
) maxes
Since the relationship between levelA,levelB,levelC is not clear.I have assumed you want to return the max(name) in case the corresponding value is not available.The Sql Fiddle here.You can replace
order by my_table.name into order by unique_seq_column and also max(name) name by the name value in max(unique_seq_column) if it suits your requirement
--Get the max count and max name for each level
with cnt_max1 as (select max(name) name ,count(1) cnt from my_table where depth=1)
,cnt_max2 as (select max(name) name ,count(1) cnt from my_table where depth=2)
,cnt_max3 as (select max(name) name ,count(1) cnt from my_table where depth=3)
--find out the total rows required
,greatest_cnt as (select greatest(cnt_max1.cnt,cnt_max2.cnt,cnt_max3.cnt) cnt from cnt_max1,cnt_max2,cnt_max3)
--Establish relationship between levelA,levelB,levelC using sublevel column
,level_A as (select * from (select rownum sublevel, my_table.name as levela from my_table where depth=1 order by my_table.name)
union
select level+cnt_max1.cnt sublevel,cnt_max1.name levela
from cnt_max1,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max1.cnt))
,level_B as (select * from (select rownum sublevel, my_table.name as levelb from my_table where depth=2 order by my_table.name)
union
select level+cnt_max2.cnt sublevel,cnt_max2.name levelb
from cnt_max2,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max2.cnt))
,level_C as (select * from (select rownum sublevel, my_table.name as levelc from my_table where depth=3 order by my_table.name)
union
select level+cnt_max3.cnt sublevel,cnt_max3.name levelc
from cnt_max3,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max3.cnt))
--Display the data
select levela,levelb,levelc
from level_A join level_b
on level_A.sublevel=level_B.sublevel
join level_c on level_C.sublevel=level_b.sublevel

Is it possible to JOIN a table on the TOP 1 if there is no unique identifier?

For Example
SELECT
a.SomethingInCommon,
tbl1.Status AS Status1,
tbl2.Status AS Status2,
tbl3.Status AS Status3
FROM Maintable a
LEFT OUTER JOIN SecondTable tbl1 ON
tbl1.ID = (SELECT TOP 1 ID
FROM SecondTable SomethingInCommon = a.SomethingInCommon)
LEFT OUTER JOIN SecondTable tbl2 ON
tbl2.ID = (SELECT TOP 1 ID
FROM SecondTable WHERE SomethingInCommon = a.SomethingInCommon
AND ID NOT IN (SELECT TOP 1 ID
FROM SecondTABLE
WHERE SomethingInCommon = a.SomethingInCommon))
LEFT OUTER JOIN SecondTable tbl3 ON
tbl23.ID = (SELECT TOP 1 ID
FROM SecondTable
WHERE SomethingInCommon = a.SomethingInCommon
AND ID NOT IN (SELECT TOP 2 ID
FROM SecondTABLE WHERE SomethingInCommon = a.SomethingInCommon))
This query joins SecondTable three times to show a record like
SomethingInCommon | Status1 | Status2 | Status 3
Is there anyway to accomplish these results if SecondTable does not have the unique identifier column (ID) ?
Perhaps maybe creating a temporary unique ID on the fly?
If you don't have IDs but know the order you want, you could create artificial IDs using ROW_NUMBER() and then do your TOP 1's off of that.
WITH TEMP AS (
SELECT 3 a, 1 b UNION ALL
SELECT 2, 1 UNION ALL
SELECT 1, 1 UNION ALL
SELECT 2, 2 UNION ALL
SELECT 1, 2)
SELECT A, B, ROW_NUMBER() OVER (PARTITION BY B ORDER BY B ASC) as RowNumber FROM TEMP
;WITH TEMP AS (
SELECT 3 a, 1 b UNION ALL
SELECT 2, 1 UNION ALL
SELECT 1, 1 UNION ALL
SELECT 2, 2 UNION ALL
SELECT 1, 2)
SELECT A, B, ROW_NUMBER() OVER (ORDER BY A ASC) as RowNumber FROM TEMP
As Raphael said in the comment, this can be done with CTE like below
with cte
as
(
SELECT M.SomethingInCommon, S.ID, ROW_NUMBER() OVER ( Partition by S.SomethingInCommon ORDER BY S.ID desc) as rn
FROM Maintable M
LEFT JOIN SecondTable S
on M.SomethingInCommon = S.SomethingInCommon
)
SELECT cte.SomethingInCommon
case when rn =1 then cte.ID end as Status1,
case when rn =2 then cte.ID end as Status2,
case when rn =3 then cte.ID end as Status3
where rn <=3
If you want the top three statuses, then you can use conditional aggregation:
select m.somethingincommon,
max(case when seqnum = 1 then status end) as status1,
max(case when seqnum = 2 then status end) as status2,
max(case when seqnum = 3 then status end) as status3
from maintable m left join
(select s.*,
row_number() over (partition by s.somethingincommon order by (select NULL)) as seqnum
from secondtable
) s
on m.somethingincommon = s.somethingincommon
group by m.somethingincommon;
If you prefer, you can do this with multiple joins:
with s as (
select s.*,
row_number() over (partition by s.somethingincommon order by (select NULL)) as seqnum
from secondtable
)
select m.*, s1.status as status, s2.status as status2, s3.status as status3
from maintable m left join
s s1
on m.somethingincommon = s1.somethingincommon and
s1.seqnum = 1 left join
s s2
on m.somethingincommon = s2.somethingincommon and
s2.seqnum = 2 left join
s s3
on m.somethingincommon = s3.somethingincommon and
s3.seqnum = 3;

Count Customers based on item master

I need you to help me on writing two queries in SQL Server 2008 that shows the following information based on item master:
Brand wise count on customer master plus customer who purchased the brand
Item Wise count of customer master plus customer who purchased the item
Here the link that shows the table information and the query which I tried.
Click here to view the table in SQL Fiddle
SELECT
brandname,
division,
route,
DivisionTotalCustomersCount = MAX(DivisionTotalCustomersCount),
RouteTotalCustomersCount = MAX(RouteTotalCustomersCount),
PurchasedCustomersCount = SUM(PurchasedCustomersCount)
FROM
(SELECT
i.brandname,
c.division,
c.route,
DivisionTotalCustomersCount =
(SELECT COUNT(distinct x.CustomerID)
FROM CustomerMaster x
WHERE x.division = c.division),
RouteTotalCustomersCount =
(SELECT COUNT(distinct x.CustomerID)
FROM CustomerMaster x
WHERE x.Route = c.route),
PurchasedCustomersCount = count(distinct C.CustomerID)
FROM CustomerMaster c
LEFT OUTER JOIN SalesData s on c.CustomerID = s.CustomerID
right outer join ItemMaster i on s.item = i.itemcode
GROUP BY i.brandname, c.division, c.route) A
GROUP BY
brandname, division, route
ORDER BY 1
Result Should as below
Excelsheet
I think you need to go reconsider the report and maybe splitting it out into multiple reports.
It does not make sense to have a route count as well as a divisional count if they are counting things at different levels of aggregation. So have a route count and division count report.
Either way, division and route is going to be null for 100PLUS because there are no customers for that brand which means there is no route or division info available.
--Division Count
SELECT BrandName, Division, COUNT(CustomerMaster.CustomerID) [Customer Count]
FROM ItemMaster LEFT OUTER JOIN
SalesData ON ItemMaster.BrandName = SalesData.Brand LEFT OUTER JOIN
CustomerMaster ON SalesData.CustomerID = CustomerMaster.CustomerID
GROUP BY BrandName, Division
--Route Count
SELECT BrandName, Route, Division, COUNT(CustomerMaster.CustomerID) [Customer Count]
FROM ItemMaster LEFT OUTER JOIN
SalesData ON ItemMaster.BrandName = SalesData.Brand LEFT OUTER JOIN
CustomerMaster ON SalesData.CustomerID = CustomerMaster.CustomerID
GROUP BY BrandName, Route, Division
Using your sqlfiddle data there are 25 sales records & 18 distinct brand/ division/ route/ customer records and there are no sales invloving 100PLUS
select
B.BrandName
, V.Division
, coalesce(Brand_count,0) as Brand_count
, coalesce(Division_count,0) as Division_count
from (select distinct BrandName from ItemMaster) as B
cross join (select distinct Division from CustomerMaster) as V
left join (
select
Brand
, Division
, sum(cust_count) over (partition by Brand) as Brand_count
, sum(cust_count) over (partition by Division) as Division_count
from (
select
S.Brand
, C.Division
, count(distinct S.CustomerID) cust_count
from salesdata as S
inner join CustomerMaster as C
on S.CustomerID = C.CustomerID
inner join ItemMaster as I
on S.item = I.ItemCode
group by
S.Brand
, C.Division
) as S
) as D
on B.BrandName = D.Brand
and V.Division = D.Division
order by
B.BrandName
, V.Division
;
| BRANDNAME | DIVISION | BRAND_COUNT | DIVISION_COUNT |
|-----------|----------|-------------|----------------|
| 100PLUS | Dubai | 0 | 0 |
| 100PLUS | RAK | 0 | 0 |
| KITCO | Dubai | 9 | 11 |
| KITCO | RAK | 9 | 7 |
| Red Bull | Dubai | 9 | 11 |
| Red Bull | RAK | 9 | 7 |
http://sqlfiddle.com/#!3/fecb0/27
All Credit to #kevriley
select
A.BrandName,
B.Division,
B.Route,
B.DivisionTotalCustomers,
B.RouteTotalCustomers,
isnull(C.PurchasedCustomersCount,0) as PurchasedCustomersCount
from
(
select distinct
BrandName, Route, Division
from dbo.ItemMaster
cross join dbo.CustomerMaster
) A
join
(
select distinct
Division,
Route,
DENSE_RANK() over (partition by Division order by c.CustomerID asc) + DENSE_RANK() over (partition by Division order by c.CustomerID desc) - 1 as DivisionTotalCustomers ,
DENSE_RANK() over (partition by ROUTE order by c.CustomerID asc) + DENSE_RANK() over (partition by ROUTE order by c.CustomerID desc) - 1 as RouteTotalCustomers
from CustomerMaster c
left join SalesData s on c.CustomerID = s.CustomerID
) B on B.Division = A.Division and B.Route = A.Route
left join
(
select
s.brand,
c.division,
c.route,
PurchasedCustomersCount = count(distinct C.CustomerID)
FROM CustomerMaster c
JOIN SalesData s on c.CustomerID = s.CustomerID
--join ItemMaster i on s.item = i.itemcode
GROUP by s.brand, c.division, c.route
) C on A.Brandname = C.Brand and C.Division = A.Division and C.Route = A.Route
See the same on SQL Fiddle
Select B.Brandname,B.Division,C AS DivisionTotalCustomerCount,
ISNULL(T.Count,0) AS PURCHASEDCUSTOMERSCOUNT
from
(
Select CM.Division,M.BrandName,COUNT(distinct CM.CustomerID) AS C
from dbo.CustomerMaster CM
CROSS JOIN ItemMaster M
GROUP BY CM.Division,M.BrandName
)B
LEFT JOIN
(Select Division,Brand,COUNT(Distinct C.CustomerID) As Count from CustomerMaster C
JOIN salesdata D
On C.CustomerID=D.CustomerID
where D.Brand='Red Bull'
GROUP BY Division,Brand
)T
ON B.Brandname=T.Brand
and B.Division=T.Division
Order by 1,2

only using select in sql instead of group by

I have this table:
supplier | product | qty
--------------------------
s1 | p1 | 300
s1 | p2 | 90
s2 | p3 | 89
I want to find suppliers with more than 2 products.
But only with select and where, no group by. Any suggestion?
Why would you want not to use group by is beyond me, but this might work:
SELECT Supplier FROM table outer WHERE
(
select count(Products) from table inner
where inner.Supplier = outer.Supplier
) > 2
Please bear in mind, that group by is made for stuff like that and should be used.
;WITH
sequenced_data AS
(
SELECT
supplier,
ROW_NUMBER() OVER (PARTITION BY supplier ORDER BY product) AS supplier_product_ordinal
FROM
YourTable
)
SELECT
supplier
FROM
sequenced_data
WHERE
supplier_product_ordinal = 3
But I'd expect it to be slower than using GROUP BY.
SELECT DISTINCT
supplier
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourTable AS lookup WHERE supplier = yourTable.supplier AND product < yourTable.product)
AND EXISTS (SELECT * FROM yourTable AS lookup WHERE supplier = yourTable.supplier AND product > yourTable.product);
In the usual parts and suppliers database, this relvar is named SP:
SELECT DISTINCT T1.SNO
FROM SP AS T1
JOIN SP AS T2
ON T1.SNO = T2.SNO
AND T2.PNO <> T1.PNO
JOIN SP AS T3
ON T1.SNO = T3.SNO
AND T3.PNO <> T1.PNO
AND T3.PNO <> T2.PNO;
Noting that you can use HAVING without GROUP BY:
SELECT DISTINCT T1.SNO
FROM SP AS T1
WHERE EXISTS (
SELECT 1
FROM SP AS T2
WHERE T2.SNO = T1.SNO
HAVING COUNT(*) > 2
);
;WITH T AS
(
SELECT *,
COUNT(*) OVER (PARTITION BY S) AS Cnt
FROM YourTable
)
SELECT DISTINCT S
FROM T
WHERE Cnt > 2
with subquery:
select distinct supplier
from table a
where (select count(*)
from table b
where b.supplier = a.supplier and b.product <> a.product
) > 1