I've just started learning SQL. Here is part of my Database:
I want to get the project name from the Project table with condition:
name = 'turbine' and value = '03' in Parameter table.
I have wrote the following query and it seems to work!
But I was wondering if any smarter query can do the job for me :
SELECT name
FROM Project
WHERE id IN (
SELECT projectId
FROM Cases
WHERE id IN (
SELECT caseId
FROM ParamValue
WHERE parameterId IN (SELECT id FROM Parameter WHERE name = 'turbine')
AND value = '03')
)
;
Instead of several nested IN clause with subquery seems more easy to read a proper set of inner join
select distinct Project.name
from Project
INNER JOIN Cases ON Cases.projectId = Project.id
INNER JOIN ParamValue ON ParamValue.caseId = Cases.id
AND ParamValue.value ='03'
INNER JOIN Parameter ON ParamValue.parameterId = Parameter.id
AND Parameter.name = 'turbine'
Sure here you go without subqueries:
SELECT pj.Name
FROM Parameter p
INNER JOIN ParamValue pv ON pv.Value = '03' AND p.Id = pv.parameterId
INNER JOIN Cases c ON pv.caseId = c.Id
INNER JOIN Project pj ON c.projectId = pj.Id
WHERE p.name = 'turbine'
;
select pr.name from Project pr
left join Cases c on pr.name = c.id
left join ParamValue pv on c.id = pv.parameterId
left join Parameter p on p.id = pv.parameterId
where p.name = 'turbine' and pv.value = '03';
Related
UPDATE sub_categories
set creatives_count =
( SELECT COUNT(services.sub_category_id)
from sub_categories
left
join services
on (sub_categories.id = services.sub_category_id)
GROUP
BY sub_categories.id);
BlockquoteError: Table 'sub_categories' is specified twice, both as a target for 'UPDATE' and as a separate source for data.
How can I update my column with the result I obtained in subquery.
You can try below -
UPDATE sub_categories s
join
(
SELECT COUNT(services.sub_category_id) as cnt from sub_categories
left join services on sub_categories.id = services.sub_category_id
GROUP BY sub_categories.id
)A on s.id=A.id
set creatives_count = a.cnt
You could use inner join on the subquery
UPDATE sub_categories
INNER JOIN (
SELECT sub_categories.id my_id, COUNT(services.sub_category_id) my_count
FROM sub_categories
LEFT JOIN services ON sub_categories.id = services.sub_category_id
GROUP BY sub_categories.id) t ON t.my_id = sub_categories.id
SET creatives_count = t.my_count
I've been given a question using AdventureWorks to list all the vendors with no products. When I run my SELECT statement nothing is returned. What am I doing wrong? (Answer can only be done using Joins and Unions - No Subqueries)
SELECT DISTINCT pv.Name AS 'Vendors'
FROM Purchasing.Vendor pv
INNER JOIN Purchasing.ProductVendor ppv
ON pv.BusinessEntityID = ppv.BusinessEntityID
INNER JOIN Production.Product pp
ON pp.ProductID = ppv.ProductID
WHERE pp.ProductID != ppv.ProductID;
You're looking at one too many tables, all ProductVendors have Products. Not all Vendors have ProductVendors.
From there you can simply use a LEFT JOIN and look for null records.
SELECT DISTINCT v.Name
FROM Purchasing.Vendor v
LEFT JOIN Purchasing.ProductVendor pv ON pv.BusinessEntityID = v.BusinessEntityID
WHERE pv.BusinessEntityID IS NULL
Use left join to include cases where there is no product for a "vendor". Inner join will consider only those cases, where a product id exists for a vendor.
Now, do grouping on a "Vendor" and count the number of products using COUNT() function.
Finally, filter out those vendors where count is zero, using HAVING clause
Try the following:
SELECT pv.Name AS 'Vendors',
Count(pp.ProductID) AS count_products
FROM Purchasing.Vendor pv
LEFT JOIN Purchasing.ProductVendor ppv
ON pv.BusinessEntityID = ppv.BusinessEntityID
LEFT JOIN Production.Product pp
ON pp.ProductID = ppv.ProductID
GROUP BY pv.Name
HAVING count_products = 0;
SELECT DISTINCT pv.Name AS 'Vendors'
FROM Purchasing.Vendor pv
INNER JOIN Purchasing.ProductVendor ppv
ON pv.BusinessEntityID = ppv.BusinessEntityID
where not exists (SELECT 1 Production.Product pp Where pp.ProductID = ppv.ProductID)
Return all vendors wich donot have any product
Using this query to get the products with words that fulfill all three required word terms (lenovo, laptop, computer):
SELECT t1.id, t1.name, t1.price FROM
(SELECT p.id AS productid, name, price
FROM products p JOIN productwords pw ON p.id = pw.productid
JOIN words w ON pw.wordid = w.id WHERE word.term = 'lenovo') t1
INNER JOIN
(SELECT p.id AS productid, name, price
FROM products p JOIN productwords pw ON p.id = pw.productid
JOIN words w ON pw.wordid = w.id WHERE word.term = 'laptop') t2
INNER JOIN
(SELECT p.id AS productid, name, price
FROM products p JOIN productwords pw ON p.id = pw.productid
JOIN words w ON pw.wordid = w.id WHERE word.term = 'computer') t3
ON
t1.productid = t2.productid
AND
t1.productid = t3.productid
ORDER BY t1.name
As far as I can see, the query considers the whole words table for each term (the tables have indexes. Database is MySql).
Can the query be rewritten in a better way, so it will become faster? (the tables contain millions of rows)
For example with subsets, so the 'laptop' search only considers the rows matching 'lenovo' - and the 'computer' search only considers the rows matching first 'lenovo' and then 'laptop'.
Thanks!
You can use the HAVING clause :
SELECT p.id AS productid, name, price
FROM products p
JOIN productwords pw ON p.id = pw.productid
JOIN words w ON pw.wordid = w.id
WHERE word.term in ('lenovo','computer','laptop')
GROUP BY p.id , name, price
HAVING COUNT(DISTINCT word.term) = 3
That is if I understood the question, it looks like product -> words is 1:n relation , and if no column from the word table is selected, that should work perfectly.
This might be a quicker way of doing it:
SELECT p.id, name, price
FROM products p
where
EXISTS (select null
from productwords pw1
JOIN words w1 ON pw1.wordid = w1.id
where w1.term = 'lenovo'
and p.id = pw1.productid )
and EXISTS (select null
productwords pw2
JOIN words w2 ON pw2.wordid = w2.id
where w2.term = 'laptop'
and and p.id = pw2.productid )
and EXISTS (select null
productwords pw3 ON p.id = pw3.productid
JOIN words w3
where w3.term = 'computer'
and p.id = pw3.productid )
ORDER BY name;
I have this query and I am getting error #1066 - Not unique table/alias: 'components'. What seems to be the issue?
SELECT COUNT(*) FROM `products`, `components`, `tradeNames`
INNER JOIN `componentsMap` ON componentsMap.product_id = product.id
INNER JOIN `components` ON componentsMap.component_id = components.id
INNER JOIN `tradeNamesMap` ON .tradeNamesMap.product_id = products.id
INNER JOIN `tradeNames` ON tradeNamesMap.tradeName_id = tradeNames.id
WHERE (((((LOWER(inci) LIKE '%abies%')
OR (trade_name.LOWER(name) LIKE '%abies%'))
OR (components.LOWER(no_cas)='abies'))
OR (components.LOWER(no_einecs)='abies'))
OR (components.LOWER(name)='abies'))
AND (`published`=1)
ORDER BY `trade_name`.`name` DESC
You don't need to list the tables before the INNER JOINs. In fact, simply don't ever use commas in the FROM clause. So:
SELECT COUNT(*)
FROM `products`
INNER JOIN `componentsMap` ON componentsMap.product_id = product.id
INNER JOIN `components` ON componentsMap.component_id = components.id
INNER JOIN `tradeNamesMap` ON tradeNamesMap.product_id = products.id
INNER JOIN `tradeNames` ON tradeNamesMap.tradeName_id = tradeNames.id
WHERE (((((LOWER(inci) LIKE '%abies%')
OR (trade_name.LOWER(name) LIKE '%abies%'))
OR (components.LOWER(no_cas)='abies'))
OR (components.LOWER(no_einecs)='abies'))
OR (components.LOWER(name)='abies'))
AND (`published`=1)
ORDER BY `trade_name`.`name` DESC;
The above query only returns one row because of the COUNT(). The order by suggests that you actually want this information for each trade_name.name. If so, you need a GROUP BY:
SELECT tn.name, COUNT(*)
FROM `products` p INNER JOIN
`componentsMap cm
ON cm.product_id = p.id INNER JOIN
`components` c
ON cm.component_id = c.id INNER JOIN
`tradeNamesMap` tnm
ON tnm.product_id = p.id INNER JOIN
`tradeNames` tn
ON tnm.tradeName_id = tn.id
WHERE ((LOWER(inci) LIKE '%abies%') OR
(tn.LOWER(name) LIKE '%abies%') OR
(c.LOWER(no_cas)='abies') OR
(c.LOWER(no_einecs)='abies') OR
(c.LOWER(name)='abies')
) AND
(`published` = 1)
GROUP BY tn.name
ORDER BY tn.`name` DESC
INNER JOIN `[components]` ON componentsMap.component_id = components.id
AND
SELECT COUNT(*) FROM `products`, [`components`], `tradeNames`
Two components are there.
Just guessing, and untested, but I suspect that something like this would do what you're after...
SELECT n.name
, COUNT(*)
FROM products p
JOIN componentsMap pc
ON pc.product_id = p.id
JOIN components c
ON c.id = pc.component_id
JOIN tradeNamesMap pn
ON pn.product_id = p.id
JOIN tradeNames n
ON n.id = pn.tradeName_id
WHERE
( inci LIKE '%abies%'
OR n.name LIKE '%abies%'
OR 'abies' IN (c.no_cas,c.no_einecs,c.name)
)
AND published = 1
GROUP
BY n.name
ORDER
BY n.name DESC
I have a stored procedure where i want to grab some data through a connection table. The database is old and have no constraint.
This is my procedure:
select distinct(p.id), pd.language, p.Company, pd.shortDescription from dbo.Category c
join dbo.ProductCategory pc on c.id = pc.CategoryId
join dbo.Product p on pc.id = p.id
join dbo.ProductDescription pd on p.id = pd.id
where
c.Company = 'Normstahl' and
c.languageid = 'en' and
p.Company = 'Normstahl' and
pc.Company = 'Normstahl' and
c.id != 'Deckenlauf' and
pd.language = 'en' and
pd.Company = 'Normstahl'
and as you can see i want to select products that is not connected to the category 'Deckenlauf'.
the problem is that if a product is connected to multiple categories i will recive the product that is connected to that category anyway since it just skips the product that is connected to the category but finds the same product that is connected to another category.
id CategoryId Company
1 Deckenlauf Normstahl
1 RGD_EUR9_DL Normstahl
this is from the connectiontable between category and product. So in my stored procedure i don't want to recieve any products with the id = 1 but now i will because if takes the second row since it is not connected to the category 'Deckenlauf'. How can i solve this problem in my stored procedure?
I solved it like this:
select distinct(p.id), pd.language, p.Company, pd.shortDescription from dbo.Category c
join dbo.ProductCategory pc on c.id = pc.CategoryId
join dbo.Product p on pc.id = p.id
join dbo.ProductDescription pd on p.id = pd.id
where
c.Company = #companyName and
c.languageid = #languageId and
p.Company = #companyName and
pc.Company = #companyName and
c.id != #categoryId and
pd.language = #languageId and
pd.Company = #companyName and
p.id not in (select id from ProductCategory where CategoryId = #categoryId)
by adding the last select
I would try using a LEFT JOIN with a condition/filter and WHERE IS NULL to check whether the product has any links to the chosen category.
While your subselect solution most definately would work and maybe be more readable the it should give you better performance.
Assuming I've understood how you use your input parameters to the stored procedure it would be something like this:
LEFT JOIN dbo.ProductCategory pc on pc.id = p.id AND pc.CategoryId = #categoryId -- Only join for the chosen category
WHERE pc.CategoryId IS NULL and -- Only include rows which did not "hit" the left join
I've updated your solution and also moved what seemed to me to be join conditions, i.e. the pd.Company and pc.Company to the ON part.
In addition I've removed the join to Category as you don't seem to use it in the select.
Which gives the following query:
select distinct(p.id), pd.language, p.Company, pd.shortDescription
from dbo.Product p
left join dbo.ProductDescription pd on p.id = pd.id AND pd.Company = p.Company AND pd.languageid = #languageId
left join dbo.ProductCategory pc on pc.id = p.id AND pc.Company = p.Company AND pc.CategoryId = #categoryId
where pc.CategoryId IS NULL -- No matching category exists
and p.Company = #companyName
Note that the left join to ProductDescription is there to include rows in the result even for products that do not have a description for the supplied #languageid, as this is how the query in your solution would work.
This could/should be changed to an inner join if products should only be included in the result if they do have a description.