PL SQL query to find a list of identifiers that matches all values in a list - sql

Let's assume I have a table that has the following data:
SHELF_ID PRODUCT_ID
shelf1 product1
shelf1 product2
shelf1 product3
shelf2 product1
shelf2 product2
shelf3 product1
I made a query 'queryA' that returns from another table
PRODUCT_ID
product1
product2
Now I want to use the 'queryA' in another query to identify which shelfs have at least all the products returned in 'queryA'
by looking at the first table you easily realize its shelf1 and shelf2, but how do I make this in PL SQL?
Thank you

I think this query can help you to find what you want :
Basically what did I do :
First I did COUNT(DISTINCT ) to product_id for each shelf and then checked if this count equal or greater than queryA product list that's mean the shelf products match.
Then I excluded products if they don't match with queryA with using EXISTS() . If you want to see also not matched products you don't need to use that filter.
--DROP TABLE shelves;
CREATE TABLE shelves
(
SHELF_ID VARCHAR(100)
,PRODUCT_ID VARCHAR(100)
);
INSERT INTO shelves
VALUES
('shelf1','product1')
,('shelf1','product2')
,('shelf1','product3')
,('shelf2','product1')
,('shelf2','product2')
,('shelf3','product1');
--DROP TABLE queryA;
CREATE TABLE queryA
(
PRODUCT_ID VARCHAR(100)
);
INSERT INTO queryA VALUES ('product1'),('product2');
SELECT *
FROM shelves S
WHERE S.SHELF_ID IN (
SELECT S.SHELF_ID
--,COUNT(DISTINCT S.PRODUCT_ID) PRODUCTCOUNT
FROM shelves S
GROUP BY S.SHELF_ID
HAVING COUNT(DISTINCT S.PRODUCT_ID)>=(SELECT COUNT(DISTINCT PRODUCT_ID)
FROM queryA Q )
)
AND EXISTS (SELECT 1
FROM queryA Q
WHERE S.PRODUCT_ID = Q.PRODUCT_ID
)

You can do this as:
with products as (
<your query here)
)
select s.shelf_id
from shelves s join
products p
on s.product_id = p.product_id
group by s.shelf_id
having count(distinct s.product_id) = (select count(*) from products);
Basically, this counts the number of matches on each shelf, and makes sure that the count of matches is the total count of products.
If there are no duplicates in shelves, you can use having count(*) = . . .).

You can do this - no aggregation needed. (Other than the SELECT DISTINCT in the top query; it would be MUCH better if you had an additional table, SHELVES, with SHELF_ID as primary key, then the query could be much more efficient.)
select distinct shelf_id
from shelves s
where not exists (select product_id
from queryA -- Your existing query goes here
where (s.shelf_id, product_id)
not in (select shelf_id, product_id
from shelves
)
)
;

Related

Finding max count of product

I am trying to find max count of product. The result must only display the brands which have max number of products in it. Can anyone suggest a better way to display result.
Here are the table details:
create table Brand (
Id integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL
)
create table Product (
ID integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Price integer NOT NULL,
BrandId integer NOT NULL,
CONSTRAINT BrandId FOREIGN KEY (BrandId)
REFERENCES Brand(Id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I have used this SQL query below but I am seeing an error below. The result must display the list of the brands that have max number of products as compared to other brands.
select count(p.id) as count, b.name AS brand_name from product p join brand b on b.Id = p.BrandId
where count(p.id) = (select max(count) from product)
group by b.name
ERROR: aggregate functions are not allowed in WHERE
LINE 2: where count(p.id) = (select max(count) from product)
^
SQL state: 42803
Character: 105
In Postgres 13+, you can use FETCH WITH TIES:
select count(*) as count, b.name AS brand_name
from product p join
brand b
on b.Id = p.BrandId
group by b.name
order by count(*) desc
fetch first 1 row with ties;
In older versions you can use window functions.

SQL Server: Query for products with matching tags

I have been pondering over this for the past few hours but I cannot find a solution.
I have a products in a table, tags in another table and a product/tag link table.
Now I want to retrieve all products which have the same tags as a certain product.
Here are the tables (simplified):
PRODUCT:
id varchar(36) (primary key)
Name varchar(50)
TAG:
id varchar(36) (primary key)
Name varchar(50)
PRODUCTTAG:
id varchar(36) (primary key)
ProductID varchar(36)
TagID varchar(36)
I find quite a few answers here on Stackoverflow talking about returning full and partial matches. However I am looking for a query which only gives full matches.
Example:
Product A has tags 1, 2, 3
Product B has tags 1, 2
Product C has tags 1, 2, 3
Product D has tags 1, 2, 3, 4
If I query for product A, only product C should be found - as it is the only one having exactly the same tags.
Is this even possible?
Yes, yes, try this way:
with aa as (
select count(*) count
from [PRODUCTTAG]
where ProductID = '19A947C0-6A0F-4A6F-9675-48FBE30A877D'
), bb as
(
select ProductID, count(*) count
from [PRODUCTTAG]
group by ProductID
)
select distinct b.ProductID
from [dbo].[PRODUCTTAG] a join
[dbo].[PRODUCTTAG] b on a.TagID = b.TagID cross join
aa join
bb on aa.count = bb.count and b.ProductID = bb.ProductID
where a.ProductID = '19A947C0-6A0F-4A6F-9675-48FBE30A877D'
declare #PRODUCTTAG table(id int identity(1,1),ProductID int,TagID int)
insert into #PRODUCTTAG VALUES
(1,1),(1,2),(1,3)
,(2,1),(2,2)
,(3,1),(3,2),(3,3)
,(4,1),(4,2),(4,3),(4,4)
;With CTE as
(
select ProductID,count(*)smallCount
FROM #PRODUCTTAG
group by ProductID
)
,CTE1 as
(
select smallCount, count(smallCount)BigCount
from cte
group by smallCount
)
,CTE2 as
(
select * from cTE c
where exists(
select smallCount from cte1 c1
where BigCount>1 and c1.smallCount=c.smallCount
)
)
select * from cte2
--depending upon the output expected join this with #PRODUCTTAG,#Product,#Tag
--like this
--select * from #PRODUCTTAG PT
--where exists(
--select * from cte2 c2 where pt.productid=c2.productid
--)
Or Tell what is final output look like ?
This is a case where I find it simpler to combine all the tags into a single string and compare the strings. But, that is painful in SQL Server until 2016.
So, there is a set based solution:
with pt as (
select pt.*, count(*) over (partition by productid) as cnt
from producttag pt
)
select pt.productid
from pt join
pt pt2
on pt.cnt = pt2.cnt and
pt.productid <> pt2.productid and
pt.tagid = pt2.tagid
where pt2.productid = #x
group by pt.productid, pt.cnt
having count(*) = pt.cnt;
This matches every product to your given product based on the tags. The having clause then ensures that the number of matching tags is the same for the two products. Because the join only considers matching tags, all the tags are the same.

how can select unique row using where/having cluse and compare with another table

i cant understand how can take unique column (remove duplication) from a table
which compare with another table data.
in my case
i have two table
i want to get unique rows from tblproduct after compireing with tblviewer as
[in table viewer first taking viewerid after that taking productid in viewer table afterthat compire with tblproduct.
actualy like that
if i take vieweris=123 two row productid select 12001&11001 after that this tblproduct productid and finaly taking the row from tblproduct which maching.
select *
from tblproduct
where productid =
(
select distinct(productid)
from tblviewer
where viewerid = 123
)
There are a few ways to do this. You can do a standard INNER JOIN to the table to filter the results:
Select Distinct P.*
From tblProduct P
Join tblViewer V On V.ProductId = P.ProductId
Where V.ViewerId = 123
Alternatively, you could use EXISTS as well - this eliminates the need to use a DISTINCT altogether:
Select *
From tblProduct P
Where Exists
(
Select *
From tblViewer V
Where V.ProductId = P.ProductId
And V.ViewerId = 123
)
Or, you could also use an IN, as suggested by the other answers:
Select *
From tblProduct
Where ProductId In
(
Select ProductId
From tblViewer
Where ViewerId = 123
)
I think you just want to use an IN clause, you will not need to use distinct
select *
from tblproduct
where productid in
(
select productid
from tblviewer
where viewerid = 123
)
I'm not sure what you're asking, but I think it is,
select *
from tblproduct
where productid in
(
select distinct(productid)
from tblviewer
)

SubQuery returning Multiple rows

I have got Two Tables
Product (Id, Name, CCode)
Category (CCode, CatName) - No Primary Key
Insert Into ProductNew (DW_Prod_Id, ProdId, ProdName, CC, CName)
Select Dw_Prod_Id.Nextval, Id, Name, CCode,
(Select CatName
From Category cc, Product p
Where cc.CCode IN p.CatCode
Group By CatName )
From Product;
SQL Error: ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
I am getting the above error Because my SubQuery returns more than one row.
I would like to Match the CatCode of each row from Product table to the Category Table so that I can obtain the CatName and then Insert rows into my New Table :)
if product can have only one category :
INSERT INTO ProdcutNew (DW_Prod_Id, ProdId, ProdName, CC, CName)
(SELECT Dw_Prod_Id.Nextval, p.Id, p.Name, cc.CCode, cc.CName
FROM Product p
INNER JOIN Category cc on p.CatCode = cc.CCode)
And you can correct your table name
ProdcutNew
to ProductNew ;)
EDIT :
But if, as #Gordon Linoff pointed, you have duplicates CCode, this won't work.
If you don't want a primary key on Category table, add at least a unique constraint (you'll have to clean your datas first)
ALTER TABLE Category ADD CONSTRAINT Unique_code UNIQUE(CCode);
EDIT 2 :
But the proper way would be :
Add an Id in Category as PK, and use it as Category_ID FK in Product (if CCode can change)
With the unique constraint on CCode.
You appear to have dulicates in your category table; otherwise a simple join would suffice:
select p.*, c.ccode
from Category c join
Product p
on c.ccode = p.catcode
To choose one category arbitrarily, do something like:
select p.*, c.ccode
from (select c.*
from (select c.*, row_number() over (partition by c.ccode order by c.ccode) as seqnum
from Category c
) c
where seqnum = 1
) c join
Product p
on c.ccode = p.catcode

complex sql query from 4 tables

I am developing an online travel guide with a lot of hotels. Each hotel belongs to a specific category, has a lot room types and each of hotel room has different price per season. I want to make a complex query from 4 tables in order to get the total number of hotels per hotels category where the minimum price of each hotel rooms is between 2 values which are adjusted by a slider.
My tables look like:
Categories
id_category
category_name
Hotels
id_hotel
hotel_name
category_id
......
hotels_room_types
id_hotels_room_type
hotel_id
room_type_id
......
hotels_room_types_seasons
hotels_room_types_id
season_id
price
......
for example some values of category_name are: Hotels, apartments, hostels
I would like my results table to have two fields like the following:
Hotels 32
apartments 0
hostels 5
I tried the following query but it returns the total number of all hotels per category, not the number of hotels where the minimum price of their rooms is between the price range.
SELECT c.category_name, count( DISTINCT id_hotel ) , min( price ) min_price
FROM categories c
LEFT JOIN hotels w ON ( c.id_category = w.category_id )
LEFT JOIN (
hotels_room_types
INNER JOIN hotels_room_types_seasons ON hotels_room_types.id_hotels_room_types = hotels_room_types_seasons.hotels_room_types_id)
ON w.id_hotel = hotels_room_types.hotel_id
GROUP BY c.category_name
HAVING min_price >=10 AND min_price <=130
Could anyone help me how to write the appropriate query?
Thanks!!!
SELECT Categories.Name, COUNT(DISTINCT ID_Hotel) [Count]
FROM Hotels
INNER JOIN Categories
ON Category_ID = ID_Category
INNER JOIN
( SELECT Hotel_ID, MIN(Price) [LowestPrice]
FROM hotels_room_types
INNER JOIN hotels_room_types_seasons
ON id_hotels_room_type = hotels_room_types_id
-- CONSIDER FILTERING BY SEASON HERE
GROUP BY Hotel_ID
) price
ON price.Hotel_ID = Hotels.ID_Hotel
WHERE LowestPrice BETWEEN 10 AND 130 -- OR WHATEVER YOUR PARAMETERS ARE
GROUP BY Categories.Name
I have no idea what RDBMS you are using but I do not know any where your query would work. The problem you were having with the Min Price (I assume) is because you are applying the logic after grouping by category, so you are counting all hotels where the category has a lowest price between 10 and 130, not where the hotel has a room with the lowest price between 10 and 130.
select
c.Category_name,
count(*) NumHotels
from
( select distinct
byRoomType.hotel_id
from
hotels_room_types_seasons bySeason
join hotels_room_types byRoomType
on bySeason.hotels_room_types_id = byRoomType.id_hotels_room_type
where
bySeason.Price between LowPriceParameter and HighPriceParameter
) QualifiedHotels
join Hotels
on QualifiedHotels.hotel_id = Hotels.id_hotel
join Categories c
on category_id = c.id_category