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

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
)

Related

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

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
)
)
;

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.

SELECT TOP inside INNER JOIN

I created this simple database in SQL Server:
create database product_test
go
use product_test
go
create table product
(
id int identity primary key,
label varchar(255),
description text,
price money,
);
create table picture
(
id int identity primary key,
p_path text,
product int foreign key references product(id)
);
insert into product
values ('flip phone 100', 'back 2 the future stuff.', 950),
('flip phone 200', 's;g material', 1400)
insert into picture
values ('1.jpg', 1), ('2.jpg', 1), ('3.jpg', 2)
What I want is to select all products and only one picture for each product. Any help is greatly appreciated.
I'm a fan of outer apply for this purpose:
select p.*, pi.id, pi.path
from product p outer apply
(select top 1 pi.*
from picture pi
where pi.product = p.id
) pi;
You can include an order by to get one particular picture (say, the one with the lowest or highest id). Or, order by newid() to get a random one.
Have you tried using a correlated sub-query?
SELECT *, (SELECT TOP 1 p_path FROM picture WHERE product = p.id ORDER BY id)
FROM picture p
Hope this helps,
SELECT
*,
(
SELECT TOP 1 p2.p_path
FROM dbo.picture p2
WHERE p.id = p2.product
) AS picture
FROM dbo.product p
Or with join:
SELECT
*
FROM dbo.product p
INNER JOIN
(
SELECT p2.product, MIN(p2.p_path) AS p_path
FROM dbo.picture p2
GROUP BY p2.product
) AS pt
ON p.id = pt.product
But you need to change p_path to varchar type
I would use a windowing function like this:
SELECT *
FROM product
JOIN (
SELECT id, product, p_path,
row_number() OVER (PARTITION BY product ORDER BY id ASC) as RN
FROM picture
) pic ON product.id = pic.product AND pic.RN = 1
As you can see here I am selecting the picture with the lowest id (ORDER BY id ASC) -- you can change this order by to your requirements.
just group by and take min or max
left join in case there is no picture
select pr.ID, pr.label, pr.text, pr.price
, min(pic.p_path)
from product pr
left join picture pic
on pic.product = pr.ID
group by pr.ID, pr.label, pr.text, pr.price

How to filter order numbers by multiple columns

I have a table with many rows that looks something like this:
Order_id Part_id Description GL_code
12345 Gk123 Gun mount 6850
12345 null Freight 4050.2
12346 Blac Lock 6790
12346 null Freight 4050
I want to make a query that returns all order information where the part_id is a GK% number or a Blac number and the order has a freight GL_code of 4050.2.
The Freight is in the description column and in a different row than the part_id.
I do not want to include all the Blac and GK% parts that do not have freight and 4050.2 on the order.
I've only been able to get all 4050.2's or all GK%'s and Blac's.
One way would be to use a correlated subquery that makes sure that there exists a row for the same order with the Freight code 4050.2:
select * from tab t
where (Part_id = 'Blac' or Part_id like 'GK%')
and exists (
select 1
from tab
where Order_id = t.Order_id
and Description = 'Freight'
and GL_code = '4050.2' -- remove the quotes if this is a number and not a char value
)
With your sample data this would return:
Order_id Part_id Description GL_code
12345 Gk123 Gun mount 6850
Another option would be to use a purely set based query:
select Order_id from tab where (Part_id = 'Blac' or Part_id like 'GK%')
intersect
select Order_id from tab where Description = 'Freight' and GL_code = '4050.2'
Or if you just want to rows with Freight you can just invert the conditions:
select * from shipper_line t
where Description = 'Freight' and GL_code = '4050.2'
and exists (
select 1
from shipper_line
where Order_id = t.Order_id
and (Part_id = 'Blac' or Part_id like 'GK%')
);
It's probably easiest to join the table to itself; SQL is better at cross-column checks than cross-row checks.
Select a.* from MyTable a
inner join MyTable b
on a.Order_ID = b.Order_ID and b.Part_ID is null and b.Description = 'Freight' and b.GL_code = 4050.2
where (a.Part_ID like 'GK%' or a.Part_ID like 'Blac%')
You can also use exists or a subquery:
select * from MyTable a
where (a.Part_ID like 'GK%' or a.Part_ID like 'Blac%')
and exists
(select 1 from MyTable b
where a.order_ID = b.order_ID and b.Description = 'Freight' and b.GL_code = 4050.2)
select * from MyTable a
where (a.Part_ID like 'GK%' or a.Part_ID like 'Blac%')
and order_ID in
(select order_ID from MyTable
where Description = 'Freight' and GL_code = 4050.2)
Edit: Here's a SQL Fiddle: http://sqlfiddle.com/#!6/a778c/1/0
please try this query. As per the your question, this seems to also fulfill the important condition:
The Freight is in the description column and in a different row than
the part_id.
select l.order_id from
(
select order_id,rownum from
(
select *, row_number() over (partition by order_id order by Part_id, Description, GL_code ) as rownum from tblt
) t
where (part_id like 'GK%' or part_id like 'Blac%') and order_id in (select order_id from tblt where GL_code like '4050.2')
) l
inner join
(select * from
(
select *, row_number() over (partition by order_id order by Part_id, Description, GL_code ) as rownum
from tblt
) t2
where t2.description like 'Freight') r
on l.order_id=r.order_id and l.rownum<>r.rownum
fiddle link: http://sqlfiddle.com/#!6/620c05/8

Multiple unique ways of executing a simple query (ORACLE)?

I have to come up with 5 different ways (unique execution plans) to process the following query.
Find the items that are delivered by all suppliers.
My database holds the following tables:
QSPL – it holds a list of supplier names
SPLNO (number)
SPLNAME (varchar)
QDEL– it holds delivery items, suppliers, and departments
DELNO (number)
DELQTY (number)
ITEMNAME (varchar)
DEPTNAME (varchar)
SPLNO (number)
QITEM – it holds list of items
ITEMNAME (varchar)
ITEMTYPE (varchar)
ITEMCOLOR (varchar)
I was able to successfully come up with the following four unique queries.
1.
select itemname --, etc.
from qitem
where itemname not in
(select itemname
from qitem, qspl
where (char(splno)+itemname) not in
(select char(splno)+itemname
from qdel));
2.
select itemname --,etc.
from qitem
where not exists
(select *
from qspl
where not exists
(select *
from qdel
where qdel.itemname = qitem.itemname
and Qdel.splno = qspl.splno));
3.
select a.itemname --, etc
from qitem a join qdel b on a.itemname = b.itemname
group by a.itemname
having count (distinct splno) = (select count(*) from qspl);
4.
select itemname
from qdel
group by itemname
having count (distinct splno) = (select count(*) from qspl);
I have no idea what to do for a 5th unique query.
Does anyone have a clue?
I tried to put this question in the best possible context with significant detail, feedback is greatly appreciated.
Thanks
Maybe some SQL 86 syntax:
select a.itemname --, etc
from qitem a, qdel b
where a.itemname = b.itemname
group by a.itemname
having count (distinct splno) = (select count(*) from qspl);
Or an outer join
select a.itemname --, etc
from qspl s, qdel b
WHERE s.splno (+)= b.splno
group by s.splno
having count (distinct b.splno) = (select count(*) from qspl);
This is another unique way (which I'm sure it's horribly inefficient):
select distinct splname
from (
select qi.itemname,
qs.splname,
count(distinct qi.itemname) over () as total_items,
count(distinct qd.itemname) over (partition by qd.splno) as items_per_supp
from qitem qi
left join qdel qd on qi.itemname = qd.itemname
left join qspl qs on qs.splno = qd.splno
) t
where total_items = items_per_supp
Or a variant of your #3 which will probably use a different execution plan:
with supplier_items as (
select splno, count(*) item_count
from qdel
group by splno
)
select splname
from qspl qs
join supplier_items si on qs.splno = si.splno
where si.item_count = (select count(*) from qitem);
Since this is homework, I will be obtuse: Check out the Oracle MINUS operator.