SQL query to get default values if translations are not found - sql

These are my tables:
Product:
ID PRICE NAME DESCRIPTION
1 100 laptop laptop_desc
2 200 mouse mouse_desc
Product_Translations:
PID LANG NAME DESCRIPTION
1 ch 伊吾 伊吾伊吾
Please don't worry about name and description in the Product table. We are keeping it to avoid join if default language is selected by the user.
Now I need to write a query to get all the products from Product according to the users's language with fallback that if no translations for name and description are found in Product_Translations get them from Product table. I tried couple of different ways, but couldn't make it work.
Update:
I require all the columns from Product table(In this example, I only gave 2, but my actual table has more columns). And one more restriction is that I need to generate this query using JPA Criteria API, so any SQL keywords not supported by JPA may not work for me.
Thanks for your help.

SELECT p.ID, p.Price,
COALESCE(pt.Name, p.Name) Name,
COALESCE(pt.Description, p.Description) Description
FROM Product p
INNER JOIN User u on u.ID = #MyUserID
LEFT JOIN Product_Translations pt ON pt.PID = p.ID AND pt.LANG = u.LANG

In your case, you should use LEFT JOIN
SELECT p.ID, p.PRICE, ISNULL(pt.NAME,p.NAME) AS NAME, p.DESCRIPTION
FROM Product AS p
LEFT JOIN Product_Translations AS pt ON p.ID = pt.PID
WHERE pt.LANG = #UserLang
ISNULL in hsqldb
http://hsqldb.org/doc/guide/builtinfunctions-chapt.html#bfc_general_functions

Use LEFT OUTER JOIN:
SELECT
p.id
,p.price
,ISNULL(t.name, p.name) AS translation
,ISNULL(t.description, p.description) AS description
FROM Product p
LEFT JOIN Translations t
ON p.id = t.pid
AND t.lang = ?
This will work on SQL Server if you use other DB change to COALESCE().

Related

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 ...

I need help formatting inner join command in SQL query

Here is my ERD for SQL Server:
https://c2.staticflickr.com/6/5832/23786188186_d6f1d93132_o.jpg
I need to find out which books are associated with each publisher.
USE BookStoreDB
SELECT ProductID
FROM Books
INNER JOIN [Publishers] PublisherID ON PublishersID = ProductID
I'm assuming I just didn't create the INNER JOIN command correctly?
SELECT p.PublisherID, b.ProductID
FROM Books b
INNER JOIN Publishers p ON p.PublisherID = b.PublisherID
The ON part of a join defines how the two tables are related to each other. In your case, it would be:
INNER JOIN Publishers ON (Books.PublisherID = Publishers.PublisherID)
If you put something between the name of the table, and the ON, it's treated as an alias. You can (and should) also use these aliases in your SELECT
FROM Books B
INNER JOIN Publishers P ON (B.PublisherID = P.PublisherID)
Finally, you probably want to select information that will actually tell you who the publisher is:
SELECT B.ISBN, P.CompanyName
FROM Books B
INNER JOIN Publishers P ON (B.PublisherID = P.PublisherID)

SQL JOIN using a mapping table

I have three tables:
COLLECTION
PERSON
PERSON_COLLECTION
where PERSON_COLLECTION is a mapping table id|person_id|collection_id
I now want to select all entries in collection and order them by person.name.
Do I have to join the separate tables with the mapping table first and then do a join again on the results?
SELECT
c.*,
p.Name
FROM
Collection c
JOIN Person_Collection pc ON pc.collection_id = c.id
JOIN Person p ON p.id = pc.person_id
ORDER BY p.Name
Not sure without the table schema but, my take is:
SELECT
c.*,
p.*
FROM
Person_Collection pc
LEFT JOIN Collection c
ON pc.collection_id = c.id
LEFT JOIN Person p
ON pc.person_id = p.id
ORDER BY p.name
The order you join won't break it but depending on which sql product you're using may effect performance.
You need to decide if you want ALL records from both/either table or only records which have a matching mapping entry, this will change the type of join you need to use.

Get correct result from mysql query

I have the following tables:
**products** which has these fields: id,product,price,added_date
**products_to_categories** which has these fields: id,product_id,category_id
**adverts_to_categories** -> id,advert_id,category_id
**adverts** which has these fields: id,advert_name,added_date,location
I can not execute sql that will return to me all products that are from category 14 and that are owned by advert located in London. So I have 4 tables and 2 conditions - to be from category 14 and the owner of the product to be from London. I tried many variants to execute sql but none of the results were correct.. Do I need to use Join and which Join - left, right, full? How the correct sql will look like? thank you in advance for your help and sorry for boring you :)
This is what I have tried so far:
SELECT p.id, product, price, category_id,
p.added_date, adverts.location, adverts.id
FROM products p,
products_to_categories ptc,
adverts,
adverts_to_categories ac
WHERE ptc.category_id = "14"
AND ptc.product_id=p.id
AND ac.advert_id=adverts.id
AND adverts.location= "London"
pretty basic logic
Select * from Products P
INNER JOIN Products_To_Categories PTC ON P.ID = PTC.Product_ID
INNER JOIN Adverts_to_Categories ATC ON ATC.Category_Id = PTC.Category_ID
INNER JOIN Adverts AD on AD.ID = ATC.Advert_ID
WHERE PTC.Category_ID = 14 and AD.Location = 'LONDON'
you would only need a LEFT or right join IF you wanted records from a table which didn't exist in other tables.
so for example, if you wanted all products even if a records even those without a category, then you would use a LEFT Join instead of inner.
The following statement should return all columns from the product table in category with id 14 and all adverts located in London:
select p.* from products p
inner join products_to_categories pc on p.id = pc.product_id
inner join adverts_to_categories ac on pc.category_id = ac.category_id
inner join adverts a on a.id = ac.advert_id
where pc.category_id = 14
and ac.location = 'London';
You should remember to add an index to the column location if you are doing these string-based queries very often.

Count() and left join problem

I'm having a problem with the a query which displays a list of shops with the number of products associated with it. I've been playing around with left joins etc for quite a while now but to no avail. The tables have the following structures:
Shops table containing columns: id, name
Products table containing columns: id, name, status, shop
The query is as follows:
select s.name
, p.name
, count(p.id)
from Product as p
left join Shop as s on p.shop=s.id
where p.status <> '8796107276379'
group by
s.id
I'm not getting the shops which have 0 products. How can I achieve this please?
The underlying database is MySQL.
Thanks!
Krt_Malta
You need SHOP on the LEFT side, since the right side is the one that may not have data, in this case PRODUCT.
Not only that, you need the WHERE condition as a LEFT-JOIN ON condition, so that it joins to products on the status condition and just discounts the product (while keeping shop) even if the status is not desired.
select s.name
, p.name
, count(p.id)
from Shop as s
left join Product as p on p.shop=s.id AND p.status <> '8796107276379'
group by
s.id, p.name
select s.name
, p.name
, count(p.id)
from Shop as s
left join Product as p on s.id=p.shop
where p.status <> '8796107276379'
group by
s.id
You need to add OR p.status IS NULL to your where clause.
select s.name, p.name, count(p.id)
from Shop s
left join Product p on p.shop = s.id
where (p.status <> '8796107276379' OR p.status IS NULL)
group by s.name, p.name
I suffered this gotcha too and though I am not entirely sure why, placing the predicate on the jojn itself rather than the actual main query is how to solve it.
I actually documented the whole thing, before reading this. I used a simple example with two two small tables, it explains I hope the difference, maybe it will help
http://simpleritsolutions.com/sql/left/join/problems