How can I query this as I want? - sql

I have three table like this:
Product
| ID | Name |
+----------------+
| 1 | Product A |
| 2 | Product B |
ProductDescription
| ID | Description |
+------------------------------------+
| 1 | Product A English Description |
| 2 | Product A Spanish Description |
| 3 | Product B English Description |
| 4 | Product B Spanish Description |
And finally a table to connect 2 above table
ProductProductDescription
| ProductID | ProductDescriptionID |
+----------------------------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
When I joined 3 above table normally, I got this
| ProductID | ProductDescripton |
+-------------------------------------------+
| 1 | Product A English Description |
| 1 | Product A Spanish Description |
| 2 | Product B English Description |
| 2 | Product B Spanish Description |
But I want to query into this:
| ProductID | English Description | Spanish Description |
+---------------------------------------------------------------------------+
| 1 | Product A English Description| Product A Spanish Description |
| 2 | Product B English Description| Product B Spanish Description |
Can you help me?

I think you want conditional aggregation:
select ppd.id,
max(case when pd.description like '%English%' then pd.description end) english_description,
max(case when pd.description like '%Spanish%' then pd.description end) spanish_description
from ProductProductDescription ppd
inner join ProductDescription pd on pd.id = ppd.productdescriptionid
group by ppd.id
Notes:
design-wise, it would be far better to have another column in product description table that represents the language of the description, rather than relying on the description itself
you don't need the product table to generate the expected result; but if you do, then just add another join to the query

One option would be using PIVOT Clause (if available for your DBMS) as
SELECT *
FROM
(
SELECT ppd.ProductID, pd.Description, LTRIM(REPLACE(pd.Description,p.Name)) AS Title
FROM ProductDescription pd
JOIN ProductProductDescription ppd
ON ppd.ProductDescriptionID = pd.ID
JOIN Product p
ON ppd.ProductID = p.ID
)
PIVOT
(
MAX(Description) FOR Title IN ('English Description' AS " English Description",
'Spanish Description' AS " Spanish Description")
)
Demo

Related

SQL INNER JOIN using CASE and 3 tables

I have the following tables and want show only the types that are available, while counting the products in that category.
Types
Type_Id
Name
1
Candy
2
Chocolate Bar
Products
Product_Id
Name
Type_Id
AvailabilityId
1
Chocolate Name 1
1
1
2
Chcoolate Name 1
2
2
3
Candy Name 1
2
2
Availability
Availability_Id
Name
1
Available
2
Reserved
3
Sol d
Desired Result
Type_Id
Name
TotalAvailable
1
Candy
1
2
Chocolate
2
Start with a basic JOIN between all three tables, on the related columns. Note use of table aliases t - Types, p - Products, a - Availability
SELECT *
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
Next add a WHERE clause to filter results by the availability status:
SELECT *
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
WHERE a.Name IN ('Available', 'Reserved')
Results:
Type_Id | Name | Product_Id | Name | Type_Id | Availability_Id | Availability_Id | Name
------: | :------------ | ---------: | :------------- | ------: | --------------: | --------------: | :--------
1 | Candy | 1 | Chocolate Name | 1 | 1 | 1 | Available
2 | Chocolate Bar | 2 | Chcoolate Name | 2 | 2 | 2 | Reserved
2 | Chocolate Bar | 3 | Candy Name | 2 | 2 | 2 | Reserved
Finally COUNT(*) the total rows matched, grouping by category (i.e. [Type].[Name])
SELECT t.Type_Id
, t.Name AS Type_Name
, COUNT(*) AS Total_Products
FROM Types t
INNER JOIN Products p ON p.Type_Id = t.Type_Id
INNER JOIN Availability a ON a.Availability_Id = p.Availability_Id
WHERE a.Name IN ('Available', 'Reserved')
GROUP BY t.Type_Id
, t.Name
Type_Id | Type_Name | Total_Products
------: | :------------ | -------------:
1 | Candy | 1
2 | Chocolate Bar | 2
SQL Fiddle
You can use an inner query with inner join to achieve this. Here's a SQL fiddle: http://sqlfiddle.com/#!9/42bd7f/2

Joining table on two columns only joins it on a single

How do I correctly join a table on two columns. My issue is that the result is not correct as it only joins on a single column.
This question started of in this other question: SQL query returns product of results instead of sum . I am creating a new question as there is an other issue I am trying to solve.
I join a table of materials on a table which contains multiple supply and disposal movements. Each movement references a material id. I would like to join the material on each movement.
My query:
SELECT supply_material_refer, disposal_material_refer, material_id, material_name
FROM "construction_sites"
JOIN projects ON construction_sites.project_refer = projects.project_id
JOIN addresses ON construction_sites.address_refer = addresses.address_id
cross join lateral ( select *
from (select row_number() over () as rn, *
from supplies
where supplies.supply_project_refer = projects.project_id) as supplies
full join (select row_number() over () as rn, *
from disposals
where disposals.disposal_project_refer = projects.project_id
) as disposals
on (supplies.rn = disposals.rn)
) as combined
LEFT JOIN materials material ON combined.disposal_material_refer = material.material_id
OR combined.supply_material_refer = material.material_id
WHERE (projects.project_name = 'Project 15')
ORDER BY construction_site_id asc;
The result of the query:
+-----------------------+-------------------------+-------------+---------------+
| supply_material_refer | disposal_material_refer | material_id | material_name |
+-----------------------+-------------------------+-------------+---------------+
| 1 | 1 | 1 | Materialtest |
| 2 | 1 | 1 | Materialtest |
| 2 | 1 | 2 | Dirt |
| 1 | 1 | 1 | Materialtest |
| 2 | 1 | 1 | Materialtest |
| 2 | 1 | 2 | Dirt |
| 1 | (null) | 1 | Materialtest |
| 4 | (null) | 4 | Stones |
+-----------------------+-------------------------+-------------+---------------+
An example line I have issues with:
+------------------------+-------------------------+-------------+---------------+
| supply_material_refer | disposal_material_refer | material_id | material_name |
+------------------------+-------------------------+-------------+---------------+
| 2 | 1 | 1 | Materialtest |
+------------------------+-------------------------+-------------+---------------+
A prefered output would be like:
+------------------------+----------------------+-------------------------+------------------------+
| supply_material_refer | supply_material_name | disposal_material_refer | disposal_material_name |
+------------------------+----------------------+-------------------------+------------------------+
| 2 | Dirt | 1 | Materialtest |
+------------------------+----------------------+-------------------------+------------------------+
I have created a sqlfiddle with dummy data: http://www.sqlfiddle.com/#!17/863d78/2
To my understanding the solution would be to have a disposal_material column and and supply_material column for the material names. I do not know how I can achieve this goal though...
Thanks for any help!

PostgreSQL select all from one table and join count from table relation

I have two tables, post_categories and posts. I'm trying to select * from post_categories;, but also return a temporary column with the count for each time a post category is used on a post.
Posts
| id | name | post_category_id |
| 1 | test | 1 |
| 2 | nest | 1 |
| 3 | vest | 2 |
| 4 | zest | 3 |
Post Categories
| id | name |
| 1 | cat_1 |
| 2 | cat_2 |
| 3 | cat_3 |
Basically, I'm trying to do this without subqueries and with joins instead. Something like this, but in real psql.
select * from post_categories some-type-of-join posts, count(*)
Resulting in this, ideally.
| id | name | count |
| 1 | cat_1 | 2 |
| 2 | cat_2 | 1 |
| 3 | cat_3 | 1 |
Your help is greatly appreciated :D
You can use a derived table that contains the counts per post_category_id and left join it to the post_categories table
select p.*, coalesce(t1.p_count,0)
from post_categories p
left join (
select post_category_id, count(*) p_count
from posts
group by post_category_id
) t1 on t1.post_category_id = p.id
select post_categories.id, post_categories.name , count(posts.id)
from post_categories
inner join posts
on post_category_id = post_categories.id
group by post_categories.id, post_categories.name

Count within the result set of a subquery

I have the following relations in my database:
Invoice InvoiceMeal
--------------------- ---------------------------
| InvoiceId | Total | | Id | InvoiceId | MealId |
--------------------- ---------------------------
| 1 | 22.32 | | 1 | 1 | 3 |
--------------------- ---------------------------
| 2 | 12.18 | | 2 | 1 | 2 |
--------------------- ---------------------------
| 3 | 27.76 | | 3 | 2 | 2 |
--------------------- ---------------------------
Meal Type
----------------------------------- -------------------
| Id | Name | TypeId | | Id | Name |
----------------------------------- -------------------
| 1 | Hamburger | 1 | | 1 | Meat |
----------------------------------- -------------------
| 2 | Soja Beans | 2 | | 2 | Vegetarian |
----------------------------------- -------------------
| 3 | Chicken | 2 |
-----------------------------------
What I want to query from the database is InvoiceId and Total of all Invoices which consist of at least two Meals where at least one of the Meals is of Type Vegetarian. I have the following SQL query and it works:
SELECT
i."Id", i."Total"
FROM
public."Invoice" i
WHERE
(SELECT COUNT(*)
FROM public."InvoiceMeal" im
WHERE im."InvoiceId" = i."Id" AND
(SELECT COUNT(*)
FROM public."Meal" m, public."Type" t
WHERE im."MealId" = m."Id" AND
m."TypeId" = t."Id" AND
g."Name" = 'Vegetarian') > 0
) >= 2;
My problem with this query is that I can not easily modify the condition that there must at least one vegetarien Meal. I want to be able, for example, to change it to at least two vegetarian meals. How can I achieve this with my query?
I would approach this by joining the tables together and using aggregation. The having clause can handle the conditions:
select i.Id, i.Total
from InvoiceMeal im join
Invoice i
on i.InvoiceId = im.InvoiceId join
Meal m
on im.mealid = m.mealid join
Type t
on m.typeid = t.typeid
group by i.Id, i.Total
having count(distinct im.mealid) >= 2 and
sum(case when t.name = 'Vegetarian' then 1 else 0 end) > 0;
I also see no reason to put double quotes around column names. That just makes the query harder to write and read.

How to mapping data with text multi level?

How to write sql statement?
Table_Product
+------------------+
| Product |
+------------------+
| AAA |
| ABB |
| ABC |
| ACC |
+------------------+
Table_Mapping
+---------------+---------------+
| ProductGroup | ProductName |
+---------------+---------------+
| A* | Product1 |
| ABC | Product2 |
+---------------+---------------+
I need the following result:
+------------+---------------+
| Product | ProductName |
+------------+---------------+
| AAA | Product1 |
| ABB | Product1 |
| ABC | Product2 |
| ACC | Product1 |
+------------+---------------+
Thanks,
TOM
The following query does what you describe when run from within the Access application itself:
SELECT Table_Product.Product, Table_Mapping.ProductName
FROM
Table_Product
INNER JOIN
Table_Mapping
ON Table_Product.Product = Table_Mapping.ProductGroup
WHERE InStr(Table_Mapping.ProductGroup, "*") = 0
UNION ALL
SELECT Table_Product.Product, Table_Mapping.ProductName
FROM
Table_Product
INNER JOIN
Table_Mapping
ON Table_Product.Product LIKE Table_Mapping.ProductGroup
WHERE InStr(Table_Mapping.ProductGroup, "*") > 0
AND Table_Product.Product NOT IN (SELECT ProductGroup FROM Table_Mapping)
ORDER BY 1
You would want to use CASE WHEN. Try this:
Select Product, (CASE
WHEN Product = 'AAA' THEN 'Product1'
WHEN Product = 'ABB' THEN 'Product1'
WHEN Product = 'ABC' THEN 'Product2'
WHEN Product = 'ACC' THEN 'Product1'
ELSE Null END) as 'ProductName'
from Table_Product
order by Product
If this is literally how it is uimplemented then you can
Select Product, ProductName
From Table_Product P
inner join Table_Mapping M
On M.Product_Group & '*' like P.Product
You would have to remove change your A* Product record to A Product though. I.e. you can't embed the wildcards in the record. If you want to have wildcards at the start it will run a lot slower.