SELECT one entry with two left joins. SQL - sql

Example
if two products
id name
1 product A
2 product B
And for each products I've attributes
id product_id value
1 1 1
2 1 2
3 2 3
3 2 4
And I need to select products by value of attributes.
I need products which have attributes with 1 AND 2 values.
This query doesn't work:
SELECT *
FROM product
LEFT JOIN attribute ON product.id = attribute.product_id
WHERE attribute.value = 1 AND attribute.value = 2;

Do a group by to find product id's with both 1 and 2 attributes. Select from products where product id found by that group by:
SELECT *
FROM product_table
WHERE id IN (select product_id
from attribute_table
where value in (1,2)
group by product_id
having count(distinct value) = 2)
Alternative solution, double join:
SELECT *
FROM product_table
JOIN attribute_table a1 ON product_table.id = a1.product_id
AND a1.value = 1
JOIN attribute_table a2 ON product_table.id = a2.product_id
AND a2.value = 2

SELECT *
FROM product p
LEFT JOIN attribute a ON p.id = a.product_id
WHERE a.value IN ('1','2')

To rephrase your question, you really need those products, which has both 1 and 2 within the values of their attributes:
SELECT product.*
-- , array_agg(attribute.value) attribute_values
-- uncomment the line above, if needed
FROM product
LEFT JOIN attribute ON product.id = attribute.product_id
GROUP BY product.id
HAVING array_agg(attribute.value) #> ARRAY[1, 2];

If you mean values 1 OR 2
SELECT *
FROM product p
LEFT JOIN attribute a ON p.id = a.product_id
WHERE a.value IN ('1', '2');

Related

SQL: Return boolean if there is at least 1 record between 2 tables

I have the following 2 tables:
Table 1 priceList
ID
CurrencyID
3
DF10CCE
Table 2 priceListItems
ID
priceListID
Product
1
3
DESK
I would like to write a statement in SQL to return a boolean (0 or 1) if the priceList has Items in, comparing with priceListItems based on their ID columns (For table A: ID = 3, and for Table B: priceListID = 3 )
How I can achieve that?
A simple left join and a case can fix this
select pl.*,
convert(bit, case when pli.ID is null then 0 else 1 end) as HasItems
from PriceList pl
left join PriceListItem pli on pl.ID = pli.priceListID
Note that there is no boolean type in sql server, the closest is the bit type that can only be 0/1 and many software will show it as false/true
Click on this link to see the query working
The result is
id
currencyid
HasItems
3
DF10DDE
True
4
blablabla
False
Try
WITH CTE AS
(
SELECT a.ID FROM priceList a inner JOIN priceListItems b
ON a.ID = b.priceListID
)
SELECT
CASE WHEN EXISTS (SELECT 1 from CTE) then 1 ELSE 0 END as bool
You can use outer joins :
select pl.*,
(case when pli.priceListID is not null then 1 else 0 end) as flag
from priceList pl left join
priceListItems pli
on pli.priceListID = pl.id
your data
declare #priceList table (
ID int NOT NULL
,CurrencyID VARCHAR(70) NOT NULL
);
INSERT INTO #priceList
(ID,CurrencyID) VALUES
(3,'DF10CCE');
declare #priceListItems table (
ID int NOT NULL
,priceListID int NOT NULL
,Product VARCHAR(40) NOT NULL
);
INSERT INTO #priceListItems
(ID,priceListID,Product) VALUES
(1,3,'DESK');
Use full join for distinguish the existence.
SELECT Iif(pl.id IS NULL, 0, 1)
FROM #priceListItems pli
FULL JOIN #priceList pl
ON pl.id = pli.pricelistid
-- where pl.id =3 --where condition
Note that a simple left join is not sufficient because (I assume) query is supposed to return only one record per priceList record.
Try:
select
pl.ID,
case
when pli.priceListID is null then 0
else 1
end as HasItems
from
priceList as pl
left join (select distinct priceListID from priceListItems) as pli on pli.priceListID = pl.ID
;
select coalesce(max(1), 0)
from priceList p inner join priceListItems pi on pi.priceListID = p.ID
where p.ID = X
The inner join may even be redundant given the relationship.

COUNT with a condition for a query with Group by and Join

Requirements:
Table 1:
Id Key SiteId ItemId OrderNumber
1 ABCD X BTL NULL
2 BCDE X BTL ABCD
3 CDEF X DDD BSFE
Table 2:
Id ItemId Name
1 BTL B Prd
2 DDD D Prd
Results Required:
Id ItemId Name AvailableKeys
1 BTL B Prd 1
2 DDD D Prd 0
Available Keys is calculated as the count of Items in table 1 having order numbers as null
Queries tried:
1)
SELECT ps.ItemId, COUNT(ps.ItemId) AS AvailableKeys, item.Name
FROM TABLE1 AS ps
LEFT JOIN TABLE2 as item ON item.ItemId = ps.ItemId
WHERE ps.SiteId = 'X'
GROUP BY ps.ItemId, item.Name
-- With this query, I am getting a count of the whole number of items but not the ones having order number as null (Count including even the Unavailable Items)
2)
SELECT ps.ItemId, COUNT(ps.ItemId) AS AvailableKeys, item.Name
FROM TABLE1 AS ps
LEFT JOIN TABLE2 as item ON item.ItemId = ps.ItemId
WHERE ps.OrderNumber IS NULL AND ps.SiteId = 'X'
GROUP BY ps.ItemId, item.Name
-- With this query, I am missing the items having **ZERO** available keys
Can anyone help me out in building a query to get the required result set from data.
Thanks in advance.
You want a conditional sum as follows:
SUM(CASE WHEN OrderNumber IS NULL THEN 1 ELSE 0 END) AS AvailableKeys
And the complete query:
SELECT item.id, ps.ItemId
, item.Name
, SUM(CASE WHEN OrderNumber IS NULL THEN 1 ELSE 0 END) AS AvailableKeys
FROM TABLE1 AS ps
LEFT JOIN TABLE2 as item ON item.ItemId = ps.ItemId
WHERE ps.SiteId = 'X'
GROUP BY item.id, ps.ItemId, item.Name;
Returns:
id
ItemId
ItemName
AvailableKeys
1
BTL
B Prd
1
2
DDD
D Prd
0
BTW: You may as well use an inner join unless you want to include the case when there is no match in table 2.
You are close with your first query, although the outer join is in the wrong order. Just add a match in the ON clause for the condition you want on the second table:
SELECT item.ItemId, COUNT(ps.ItemId) AS AvailableKeys, item.Name
FROM TABLE2 item LEFT JOIN
TABLE1 ps
ON item.ItemId = ps.ItemId AND
ps.SiteId = 'X' AND
ps.OrderNumber IS NULL
GROUP BY item.ItemId, item.Name;
SELECT item.id, ps.ItemId, item.Name,
SUM(IIF(OrderNumber IS NULL,1,0)) AS AvailableKeys
FROM
TABLE1 AS ps
LEFT JOIN TABLE2 AS item ON item.ItemId = ps.ItemId
WHERE ps.SiteId = 'X'
GROUP BY item.id, ps.ItemId, item.Name;

How to get two different value for same reference id in same row

I have two table:
1) Product
2) Item
Product table:
PId Name value1 value2
1 abc 1233 4567
2 xyz 9099 9099
Item Table:
itemId itemname item_start item_end
1 idc 1 2
item_start & item_end are the reference of product table on column PId.
Now when i write below query:
select * from item left join product on item_start = PId and item_end = PId
then it gives only value1 data but i want data like :
itemId itemname item_start item_start_value1 item_start_value2 item_end item_end_value1 item_end_value2
1 idc 1 1233 4567 2 9099 9099
How can i get the above output?
Join the product table twice.
SELECT item.*
, productStart.Value1 AS product_start_value1
, productStart.Value2 AS product_start_value2
, productEnd.Value1 AS product_end_value1
, productEnd.Value2 AS product_end_value2
FROM item
LEFT OUTER
JOIN product productStart
ON productStart.PId = item.item_start
LEFT OUTER
JOIN product productEnd
ON productEnd.PId = item.item_end
As you have 2 FKs on item table, you'd need 2 joins on product table.
SELECT I.itemId, I.itemname, I.item_start, P1.value1 as item_start_value1, P1.value2 as item_start_value2, I.item_end, P2.value1 as item_end_value1, P2.value2 as item_end_value2
FROM item
LEFT JOIN product P1 ON item_start = PId
LEFT JOIN product P2 ON item_end = PId
Example of what can be done:
select * from #Item i inner join #Product p on i.item_Start = p.Pid
inner join #Product p2 on i.Item_end = p2.PId
SELECT item.itemId
, item.itemname
, item.item_start
, productStart.Value1 AS item_start_value1
, productStart.Value2 AS item_start_value2
, item.item_end
, productEnd.Value1 AS item_end_value1
, productEnd.Value2 AS item_end_value2
FROM item
Inner JOIN product productStart ON productStart.PId = item.item_start
Inner JOIN product productEnd ON productEnd.PId = item.item_end

Find projects which belong to several categories

I have a projects table (columns id and name), a categories table (columns id and name) and a join table projects_categories (columns project_id and category_id).
I want to write a query that returns all the projects which belong to a set of categories. For instance if I have
projects
id name
1 foo
2 bar
categories
id name
1 bee
2 gee
projects_categories
project_id category_id
1 1
1 2
2 1
I want to write a query that returns me the project "foo" if I pass the category ids 1 and 2. I have tried the following query, but it it returns all the projects that belongs to category 1 OR 2, not category 1 AND 2.
SELECT "projects".*
FROM "projects"
INNER JOIN "projects_categories" ON "projects_categories"."project_id" = "project"."id"
WHERE "projects_categories"."category_id" IN (1, 2)
The following query does not return any result:
SELECT "projects".*
FROM "projects"
INNER JOIN "projects_categories" ON "projects_categories"."project_id" = "project"."id"
WHERE "projects_categories"."category_id" = 1
AND "projects_categories"."category_id" = 2
I understand why these queries return these results, but can't figure out how to write the query I need.
You can try with EXISTS :
SELECT "p".*
FROM "projects" "p"
WHERE EXISTS (
SELECT *
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
AND "pc"."project_id" = "p"."id"
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
)
Or JOIN :
SELECT "p".*
FROM "projects" "p"
JOIN (
SELECT "pc"."project_id"
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
) "tpc" ON "tpc"."project_id" = "p"."id"
Or IN :
SELECT "p".*
FROM "projects" "p"
WHERE "p"."id" IN (
SELECT "pc"."project_id"
FROM "projects_categories" "pc"
WHERE "pc"."category_id" IN (1, 2)
GROUP BY "pc"."project_id"
HAVING COUNT(DISTINCT "pc"."project_id" ) = 2)
)
The issue is that the query was looking into the same table and column and attempting to have two different values. This will separate the two different conditions, and then combine them into one result set.
SELECT A.ProjectName, A.CategoryId, B.CategoryId
FROM
(SELECT P.Name [ProjectName], PC.CategoryId, PC.ProjectId
FROM #Projects P
INNER JOIN #Project_Categories PC
ON PC.ProjectId = P.ID
WHERE PC.CategoryId = 1
) A
INNER JOIN (SELECT P.Name [ProjectName], PC.CategoryId, PC.ProjectId
FROM #Projects P
INNER JOIN #Project_Categories PC
ON PC.ProjectId = P.ID
WHERE PC.CategoryId = 2
) B
ON A.ProjectId = B.ProjectId

SQL Dynamic join?

Please see http://sqlfiddle.com/#!3/2506f/2/0
I have two tables. One is a general record, and the other is a table containing related documents that link to that record.
In my example I've created a straightforward query which shows all records and their associated documents. This is fine, but I want a more complex situation.
In the 'mainrecord' table there is a 'multiple' field. If this is 0, then I only want the most recent document from the documents table (that is, with the highest ID). If it is 1, I want to join all linked documents.
So, rather than the result of the query being this:-
ID NAME MULTIPLE DOCUMENTNAME IDLINK
1 One 1 first document 1
1 One 1 second document 1
2 Two 0 third document 2
2 Two 0 fourth document 2
3 Three 1 fifth document 3
3 Three 1 sixth document 3
It should look like this:-
ID NAME MULTIPLE DOCUMENTNAME IDLINK
1 One 1 first document 1
1 One 1 second document 1
2 Two 0 fourth document 2
3 Three 1 fifth document 3
3 Three 1 sixth document 3
Is there a way of including this condition into my query to get the results I'm after. I'm happy to explain further if needed.
Thanks in advance.
WITH myData
AS
(SELECT mainrecord.*, documentlinks.documentName, documentlinks.idlink,
Row_number()
OVER (
partition BY mainrecord.ID
ORDER BY mainrecord.ID ASC) AS ROWNUM
FROM mainrecord INNER JOIN documentlinks
ON mainrecord.id = documentlinks.idlink)
SELECT *
FROM mydata o
WHERE multiple = 0 AND rownum =
(SELECT max(rownum) FROM mydata i WHERE i.id = o.id)
UNION
SELECT *
FROM myData
WHERE multiple = 1
http://sqlfiddle.com/#!3/2506f/57
Another solution (tested at SQL-Fiddle):
SELECT m.*,
d.id as did, d.documentName, d.IDLink
FROM mainrecord AS m
JOIN documentlinks AS d
ON d.IDLink = m.id
AND m.multiple = 1
UNION ALL
SELECT m.*,
d.id as did, d.documentName, d.IDLink
FROM mainrecord AS m
JOIN
( SELECT d.IDLink
, MAX(d.id) AS did
FROM mainrecord AS m
JOIN documentlinks AS d
ON d.IDLink = m.id
AND m.multiple = 0
GROUP BY d.IDLink
) AS g
ON g.IDLink = m.id
JOIN documentlinks AS d
ON d.id = g.did
ORDER BY id, did ;
This will probably do:
SELECT mainrecord.name, documentlinks.documentname
FROM documentlinks
INNER JOIN mainrecord ON mainrecord.id = documentlinks.IDLink AND multiple = 1
UNION
SELECT mainrecord.name, documentlinks.documentname
FROM (SELECT max(id) id, IDLink FROM documentlinks group by IDLink) maxdocuments
INNER JOIN documentlinks ON documentlinks.id = maxdocuments.id
INNER JOIN mainrecord ON mainrecord.id = documentlinks.IDLink AND multiple = 0
How about this:
select * from mainrecord a inner join documentlinks b on a.Id=b.IDLink
where b.id=(case
when a.multiple=1 then b.id
else (select max(id) from documentlinks c where c.IDLink=b.IDLink) end)
SQL FIDDLE
select
m.ID, m.name, m.multiple, dl.idlink,
dl.documentName
from mainrecord as m
left outer join documentlinks as dl on dl.IDlink = m.id
where
m.multiple = 1 or
not exists (select * from documentlinks as t where t.idlink = m.id and t.id < dl.id)