SQL accessing child table data - sql

I have a task in which I'm using Northwind database to select only these products which category name begins with 'C'. Product details are in table Products while CategoryName is in table Categories and this is the only answer I came up with
SELECT P.*
FROM Products P, Categories C
WHERE C.CategoryName LIKE 'C%' AND P.CategoryID = C.CategoryID
I was curious if I can do it without putting Categories inside FROM clause and connecting the tables. For me it seemed logical that I can access child table and use the values inside it smh, can you explain why it's not possible?

You can use a Correlated Subquery to achieve this:
SELECT *
FROM Products p
WHERE EXISTS (SELECT id FROM Categories c WHERE c.id = p.id)
This subquery is "Correlated" in that the SQL in the subquery references the query in which it's contained. Very similar to a join, but we are down in the WHERE clause using the EXISTS condition.
EXISTS is nice since it doesn't care what is returned by the subquery. It only cares that ANYTHING was returned by the subquery. So that subquery could also be: SELECT 1 FROM Categories WHERE c.id = p.id and this query would still work.

You should be using JOINS not that type of syntax (INNER, LEFT, RIGHT, ...)
But you can do this
SELECT P.*
FROM dbo.Products P WITH (NOLOCK)
INNER JOIN dbo.Categories C WITH (NOLOCK)
ON P.CategoryID = C.CategoryID
WHERE
SUBSTRING(LTRIM(C.CategoryName), 1, 1) = 'C'
Join Products with Categories using an INNER JOIN which will only return the records that match. Add WITH (NOLOCK) when selecting from a transactional database so it does not lock users when attempting to run a transaction. I try to avoid LIKE when possible so in your scenario you can use substring to get the first character. The first argument is the expression, then start index and then how many characters you need to grab. LTRIM will remove white spaces from the left so even if your CategoryName accidentally has a leading whitespace or spaces it will remove them before running the substring. RTRIM would be unnecessary for what you need

Related

How to put conditions on left joins

I have two tables, CustomerCost and Products that look like the following:
I am joining the two tables using the following SQL query:
SELECT custCost.ProductId,
custCost.CustomerCost
FROM CUSTOMERCOST Cost
LEFT JOIN PRODUCTS prod ON Cost.productId =prod.productId
WHERE prod.productId=4
AND (Cost.Customer_Id =2717
OR Cost.Customer_Id IS NULL)
The result of the join is:
joins result
What i want to do is when I pass customerId 2717 it should return only specific customer cost i.e. 258.93, and when customerId does not match then only it should take cost as 312.50
What am I doing wrong here?
You can get your expected output as follows:
SELECT Cost.ProductId,
Cost.CustomerCost
FROM CUSTOMERCOST Cost
INNER JOIN PRODUCTS prod ON Cost.productId = prod.productId
WHERE prod.productId=4
AND Cost.Customer_Id = 2717
However, if you want to allow customer ID to be passed as NULL, you will have to change the last line to AND Cost.Customer_Id IS NULL. To do so dynamically, you'll need to use variables and generate the query based on the input.
The problem in the original query that you have posted is that you have used an alias called custCost which is not present in the query.
EDIT: Actually, you don't even need a join. The CUSTOMERCOST table seems to have both Customer and Product IDs.
You can simply:
SELECT
Cost.ProductId, Cost.CustomerCost
FROM
CUSTOMERCOST Cost
WHERE
Cost.Customer_Id = 2717
AND Cost.productId = 4
You seem to want:
SELECT c.*
FROM CUSTOMERCOST c
WHERE c.productId = 4 AND c.Customer_Id = 2717
UNION ALL
SELECT c.*
FROM CUSTOMERCOST c
WHERE c.productId = 4 AND c.Customer_Id IS NULL AND
NOT EXISTS (SELECT 1 FROM CUSTOMERCOST c2 WHERE c2.productId = 4 AND c2.Customer_Id = 2717);
That is, take the matching cost, if it exists for the customer. Otherwise, take the default cost.
SELECT custCost.ProductId,
custCost.CustomerCost
FROM CUSTOMERCOST Cost
LEFT JOIN PRODUCTS prod
ON Cost.productId =prod.productId
AND (Cost.Customer_Id =2717 OR Cost.Customer_Id IS NULL)
WHERE prod.productId=4
WHERE applies to the joined row. ON controls the join condition.
Outer joins are why FROM and ON were added to SQL-92. The old SQL-89
syntax had no support for them, and different vendors added different,
incompatible syntax to support them.

How can I get data for one field from multiple tables?

I have a column ContentID in a table that identifies content that exists in other tables. For example, it could refer to a pageID, productID, etc. My plan is to use that to pull through other information I need, such as a page name or product name. This is what I have so far:
SELECT TL.ID, TL.TableName, TL.FileName, TL.ContentID, p.PageName AS Content
FROM TranslationLog TL
LEFT JOIN Pages P ON TL.ContentID = P.PageID
LEFT JOIN Categories C ON TL.ContentID = C.CategoryID
LEFT JOIN ProductDescriptions PD ON TL.ContentID = PD.SKU
The idea is for each row, I want to get the data for the specified content using the TableName and ContentID fields. Currently, I'm able to get PageName by selecting p.PageName AS Content. However, I'd like to do this for each of the tables; if the row corresponds to the pages table, then query that table - same for categories and product descriptions. I also need it to have the alias "Content", regardless of which field from another table we're using, such as PageName or ProductName.
Is it possible to do this?
EDIT:
The solution posted by rd_nielsen was almost perfect, but it turned out there was actually a bit of overlap with the ContentID. Here's what I ended up with to fix it:
SELECT TL.ID, TL.TableName, TL.FileName, TL.ContentID, coalesce(P.PageName, C.CategoryName, PD.ProductName)
FROM TranslationLog TL
LEFT JOIN Pages P ON TL.ContentID = P.PageID AND TL.TableName = 'Pages'
LEFT JOIN Categories C ON TL.ContentID = C.CategoryID AND TL.TableName = 'Categories'
LEFT JOIN ProductDescriptions PD ON TL.ContentID = PD.SKU AND TL.TableName = 'Products'
You should use cross-apply. For example:
select
T.*
from
table_stored_data u
cross apply dbo.created_function(u.contentid,u.tablename,u.search_column) T
You need to write a function for it.
If the values of TranslationLog.ContentID can appear in only one of the related tables, then you can coalesce the values from those tables:
SELECT
TL.ID,
TL.TableName,
TL.FileName,
TL.ContentID,
coalesce(p.PageName, C.CategoryName, PD.ProductName) AS Content
FROM
...
Try concat function:
select concat(p.PageName, C.CategoryName, PD.ProductName) AS Content from ...

Query with columns from 4 tables in SQL

Can anyone who knows SQL, specifically the flavor used in Microsoft Access 2013, tell me what I'm doing wrong here?
SELECT custid, custname, ordno, itemno, itemname
FROM cust
INNER JOIN order
ON cust.custid = order.custid
INNER JOIN orderitems
ON order.ordno = orderitems.ordno
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
I've already read other, similar questions, and tried the methods they used in their solutions, but I'm getting a "Syntax error in FROM clause.", almost no matter what I try.
* * *
SOLUTION: Thanks for the replies! In addition to adding square brackets around "order" and using TableName.ColumnName syntax in SELECT, I had to use parentheses for my multiple INNER JOINs. Here is the fixed code:
SELECT cust.custid, cust.custname, [order].ordno, orderitems.itemno, inv.itemname
FROM ((cust
INNER JOIN [order]
ON cust.custid = [order].custid)
INNER JOIN orderitems
ON [order].ordno = orderitems.ordno)
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
SELECT cust.custid --<-- Use two part name here
,cust.custname
,[order].ordno
,orderitems.itemno --<-- Only guessing here use the correct table name
,inv.itemname --<-- Only guessing here use the correct table name
FROM cust
INNER JOIN [order]
ON cust.custid = [order].custid --<-- used square brackets [] around ORDER as it is
INNER JOIN orderitems -- a key word.
ON [order].ordno = orderitems.ordno
INNER JOIN inv
ON orderitems.itemno = inv.itemno;
In your Select Statament you need to use Two Part name i.e TableName.ColumnName since these column can exist in more than one Tables in your FROM clause you need to tell sql server that columns in your select coming from which table in your from clause.

What's the difference between filtering in the WHERE clause compared to the ON clause?

I would like to know if there is any difference in using the WHERE clause or using the matching in the ON of the inner join.
The result in this case is the same.
First query:
with Catmin as
(
select categoryid, MIN(unitprice) as mn
from production.Products
group by categoryid
)
select p.productname, mn
from Catmin
inner join Production.Products p
on p.categoryid = Catmin.categoryid
and p.unitprice = Catmin.mn;
Second query:
with Catmin as
(
select categoryid, MIN(unitprice) as mn
from production.Products
group by categoryid
)
select p.productname, mn
from Catmin
inner join Production.Products p
on p.categoryid = Catmin.categoryid
where p.unitprice = Catmin.mn; // this is changed
Result both queries:
My answer may be a bit off-topic, but I would like to highlight a problem that may occur when you turn your INNER JOIN into an OUTER JOIN.
In this case, the most important difference between putting predicates (test conditions) on the ON or WHERE clauses is that you can turn LEFT or RIGHT OUTER JOINS into INNER JOINS without noticing it, if you put fields of the table to be left out in the WHERE clause.
For example, in a LEFT JOIN between tables A and B, if you include a condition that involves fields of B on the WHERE clause, there's a good chance that there will be no null rows returned from B in the result set. Effectively, and implicitly, you turned your LEFT JOIN into an INNER JOIN.
On the other hand, if you include the same test in the ON clause, null rows will continue to be returned.
For example, take the query below:
SELECT * FROM A
LEFT JOIN B
ON A.ID=B.ID
The query will also return rows from A that do not match any of B.
Take this second query:
SELECT * FROM A
LEFT JOIN B
WHERE A.ID=B.ID
This second query won't return any rows from A that don't match B, even though you think it will because you specified a LEFT JOIN. That's because the test A.ID=B.ID will leave out of the result set any rows with B.ID that are null.
That's why I favor putting predicates in the ON clause rather than in the WHERE clause.
The results are exactly same.
Using "ON" clause is more suggested due to increasing performance of the query.
Instead of requesting the data from tables then filtering, by using on clause, you first filter first data-set and then join the data to other tables. So, lesser data to match and faster result is given.
There is no difference between the above two queries outputs both of them result same.
When you are using On Clause the join operation joins only those rows that matches the codidtion specified on ON Clause
Where as in case of Where Clause, the join opeartion joins all the rows and then filters out based on where condidtion Specified
So, obviously On Clause is more effective and should be preferred over where condidtion

The multi part identifier could not be bound

SELECT * FROM Products_Joined, Products
WHERE p.ProductManufacturer = 'Sony'
ORDER BY p.ProductCode
I keep getting the error The multi part identifier p.ProductManufacturer could not be bound
I tried:
Setting the Order By
Adding the PRODUCTS table to the FROM
Is there something I'm missing?
You should use:
SELECT p.*, pj.*
FROM dbo.Products p
INNER JOIN dbo.ProductsJoined pj ON ..... <== add your missing JOIN condition here
WHERE p.ProductManufacturer = 'Sony'
ORDER BY p.ProductCode
First of all: never use SELECT * in your production code.
Secondly: use the proper ANSI JOIN syntax (INNER JOIN..) to clearly show what you're joining, and on what JOIN condition (which is missing in your case - you're producing a cartesian product here.....)
Third: if you use table aliases like p. - you need to define them, too!
You have no p object. You need to alias one of your tables.
SELECT * FROM Products_Joined, Products AS p
WHERE p.ProductManufacturer = 'Sony'
ORDER BY p.ProductCode
That will fix your immediate problem, however you should have a JOIN on your tables or else you are doing a CROSS JOIN, which is usually not preferable. An example of what it would look like is below.
SELECT *
FROM Products_Joined
JOIN Products AS p
ON Products_Joined.ProductsID = p.ProductsID
--This join is a guess on what the common column is between these two tables
--Change as necessary
WHERE p.ProductManufacturer = 'Sony'
ORDER BY p.ProductCode
UPDATE BASED ON YOUR COMMENT
If you received the error even with a Products.ProductManufacturer, then you are probably missing the ProductManufacturer column in the Products table. I would check your schema and verify the column exists.