How do I collapse a result set in SQLite? - sql

Let's say I have table
id name
1 nora
2 mars
3 ven
and I left join on id with this table,
id type value
1 clothing shirt
1 clothing pants
1 toys abacus
1 toys legos
...
how do I produce something that looks like,
id name clothing toys
1 nora shirt, pants abacus, legos

A simple join does not help if you want to put different values into different result columns.
You need to use correlated subqueries:
SELECT id,
name,
(SELECT group_concat(value, ', ')
FROM Table2
WHERE id = Table1.id
AND type = 'clothing'
) AS clothing,
(SELECT group_concat(value, ', ')
FROM Table2
WHERE id = Table1.id
AND type = 'toys'
) AS toys
FROM Table1

Related

SQL get table1 names with a count of table2 and table3

I have three tables, table1 is connected to table2 and table3, but table2 and table3 are not connected. I need an output count of table2 and table3 for each table1 row. I have to use joins and a group by table1.name
SELECT Tb_Product.Name, count(TB_Offers.Prod_ID) 'Number of Offers', count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.Name
I need to combine these queries:
SELECT Tb_Product.[Name], count(TB_Offers.Prod_ID) 'Number of Offers'
FROM Tb_Product LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.[Name]
SELECT Tb_Product.[Name], count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
GROUP BY Tb_Product.[Name]
Results:
Name Number of Offers
Airplane 6
Auto 5
Bike 3
Camera 0
Computer 12
Milk 4
Oil 4
Orange 6
Telephone 0
Truck 6
TV 4
Name Number of Requests
Airplane 1
Auto 5
Bike 0
Camera 2
Computer 6
Milk 4
Oil 5
Orange 6
Telephone 0
Truck 1
TV 5
My results for offers and requests are the same value. I am not sure what I am doing wrong with the joins. Do I need to somehow join product to request and separately join product to offers? This needs to be done in one query.
This is for a class. Explanation would also be appreciated.
The simplest way to do this is to count the distinct values of each column:
SELECT
Tb_Product.Name,
count(distinct TB_Offers.Prod_ID) 'Number of Offers',
count(distinct Tb_Requests.Prod_ID) 'Number of Requests'
FROM
Tb_Product
LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY
Tb_Product.Name
This is necessary because of the way joins work consecutively to produce a rowset that is a combination of all the input relations. COUNT() normally performs a count of non-null values in a column.
You can also do something like this, which aggregates the counts from the child tables independently and then joins them to the base table:
SELECT
p.Name,
o.cnt as Offer_Count,
r.cnt as Request_Count
FROM
TB_Product p
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Offers GROUP BY Prod_ID) o
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Requests GROUP BY Prod_ID) r
More explanation...
Let's say you have two products:
Prod_ID
Name
1
Widget
2
Gizmo
And two offers, one for each product:
Offer_ID
Prod_ID
100
1
200
2
And two requests for each product:
Request_ID
Prod_ID
1001
1
1002
1
2001
2
2002
2
Now you join Product relation to Offer relation on Prod_ID, you get a result like this:
Prod_ID
Name
Offer_ID
Prod_ID
1
Widget
100
1
2
Gizmo
200
2
Now when you join that relation to Requests on Prod_ID, you get something like this:
Prod_ID
Name
Offer_ID
Prod_ID
Request_ID
Prod_ID
1
Widget
100
1
1001
1
1
Widget
100
1
1002
1
2
Gizmo
200
2
2001
2
2
Gizmo
200
2
2002
2
Now when you count any of these columns you get 4 because each column has 4 values.

join two SQL rows in a single one

I have three tables in Postgresql, for a biological classification system.
table lang (languages)
id name
1 português
2 english
-------------------------------
table taxon (biological groups)
id name
...
101 Mammalia
-------------------------------
table pop (popular names)
id tax lang pop
...
94 101 1 mamíferos
95 101 2 mammals
I want to get
id name namePT nameEN
101 Mammalia mamíferos mammals
but my join is giving me
id name pop
101 Mammalia mamíferos
101 Mammalia mammals
select t.id,name,pop from taxon t
left join pop p on p.tax = t.id
where t.id = 101
How can I get the desired result in a single row?
If you are happy to change query every time you add a new language then this query will do the trick:
select t.id,name,pe.pop as eng_pop, pp.pop as port_pop
from taxon t
left join pop pe on pe.tax = t.id and pe.lang = 1
left join pop pp on pp.tax = t.id and pp.lang = 2
where t.id = 101
You could use this
SELECT t.id, t.name,
MAX(CASE WHEN p.lang = 1 THEN p.pop END) AS namePT,
MAX(CASE WHEN p.lang = 2 THEN p.pop END) AS nameEN
FROM taxon t
LEFT JOIN pop p
ON p.tax = t.id
GROUP BY t.id, t.name;
Here's how I got the results:
with base as (
select t.id, t.name,
case when lang = 1 then 'mamiferos' else null end as namePT,
case when lang = 2 then 'mamals' else null end as nameEN
from taxon t
left join pop p on t.id = p.tax
group by 1,2,3, p.lang
)
select
distinct id,
name,
coalesce(namept,'mamiferos',null) as namept,
coalesce(nameen,'mamals',null) as nameen
from base
where id = 101
group by id, name, namept, nameen;
id | name | namept | nameen
-----+----------+-----------+--------
101 | Mammalia | mamiferos | mamals
(1 row)

City names as column title

I have two tables.
Food Table
--------------------------
ID CityID FoodName
--------------------------
1 1 FoodA
2 1 FoodB
3 1 FoodC
4 2 FoodW
5 2 FoodX
6 2 FoodY
7 2 FoodZ
City Table
--------------------------
ID CityName
--------------------------
1 Memphis
2 Nashville
3 Chattanooga
So How can I use CityName s as Column title and list the food in that city.
--------------------------------------
Memphis Nashville Chattanooga
--------------------------------------
FoodA FoodW
FoodB FoodX
FoodC FoodY
FoodZ
I'm pretty sure on that I have to use pivot but I couldn't find a good solution yet.
This is what I've achieved so far.
SELECT *
FROM (
SELECT *
FROM Food F
INNER JOIN City C ON C.ID = F.CityID
) DataTable D
PIVOT(F.FoodName FOR C.CityName IN (
[Memphis]
,[Nashville]
,[Chattanooga]
)) PivotTable
you can use this query to get your output. Actually you did some mistakes to setup the pivot query.
select Memphis,Nashville,Chattanooga
from
(
select f.ID,c.CityName,f.FoodName
from Food f
inner join City c
on f.CityID=c.id
)result
pivot
(
max(FoodName)
for CityName in(Memphis,Nashville,Chattanooga)
) as pvt
The PIVOT operator uses the columns from the data table that are not in the PIVOT definition as GROUP anchor.
That mean that two values will be in the same row of a PIVOT table when they have the same value in the columns of data table that are neither the aggregated one or the pivoted one.
The OP data don't have this value so a new partitioned id is generated.
SELECT Memphis, Nashville, Chattanooga
FROM (SELECT c.CityName, f.FoodName
, FoodID = Row_Number() OVER (PARTITION BY c.ID ORDER BY FoodName)
FROM Food f
INNER JOIN City c ON f.CityID = c.id) d
PIVOT
(MAX(FoodName) FOR CityName IN (Memphis,Nashville,Chattanooga)) pvt

One select on two tables with NULL and not null

I try to get select from two tables and put some data from one to other with ussing WHERE
(PL/SQL)
I have two tables like those:
table1
ID NAME COLOR COMPANY_SHORT_NR
1 a Green 1
2 b Red 23
3 c Blue null
4 a Green null
5 g Green 1
table2
ID SHORT COMP_NAME
1 1 company_name_1
2 23 comapny_name_2
and now I would like to get all data from table 1 with companies names and if its null get info it is null like that
1 a Green company_name_1
2 b Red comapny_name_2
3 c Blue null
4 a Green null
5 g Green company_name_1
I tried do it like this:
select ID
,NAME
,COLOR
,COMPANY_SHORT_NR
from table1
,table2
where COMPANY_SHORT_NR = SHORT
but this give me only not null values:
1 a Green company_name_1
2 b Red comapny_name_2
5 g Green company_name_1
if i use sth like this:
select ID
,NAME
,COLOR
,COMPANY_SHORT_NR
from table1
,table2
where COMPANY_SHORT_NR = SHORT or COMPANY_SHORT_NR is null
I get thousends of records ...
If I use only IS NULL than it returns me only 2 rows as it should be.
Where I make mistake ?
You have to use left join as below
select ID
,NAME
,COLOR
,COMPANY_SHORT_NR
from table1 t1
left join table2 T2 on t1.COMPANY_SHORT_NR = t2.SHORT
you neen OUTER JOIN for that
select ID
,NAME
,COLOR
,COMPANY_SHORT_NR
from table1
LEFT OUTER JOIN table2 ON ( COMPANY_SHORT_NR = SHORT )
wouldn't it be better to join the tables? like SELECT ID, NAME, COLOR, COMPANY_SHORT_NR FROM table1 t1 LEFT JOIN table2 t2 on t2.SHORT = t1.COMPANY_SHORT_NR WHERE 1

Select multiple values from same column based on 2 columns

Best explained using an example. My trials and results are bellow the example.
There are two tables (in reality I have multiple tables)
TABLE: Products
ID name
-----------
1 apple
2 orange
3 pear
TABLE: ATTRIBUTES
ID prod_ID attr_id value
----------------------------
1 1 101 20
2 1 102 red
3 1 103 sweet
4 2 101 30
5 2 102 orange
6 2 103 sour
6 3 101 40
7 3 102 green
8 3 103 sweet
DESIRED OUTPUT
name attr_id 101 AS 'price' attr_id 102 AS 'taste'
------------------------------------------------------
apple 20 sweet
orange 30 sour
pear 40 sweet
I have managed SQL till now but recently I have had to call 3 tables and combine column values like shown above. I just can't get my head wrapped around this. Help would be greatly appreciated.
Since you have not mentioned any RDBMS in your question, the query below will work on most RDBMS.
SELECT a.Name,
MAX(CASE WHEN b.attr_ID = 101 THEN b.value END) Price,
MAX(CASE WHEN b.attr_ID = 103 THEN b.value END) Taste
FROM Products a
INNER JOIN Attributes b
ON a.ID = b.prod_ID
GROUP BY a.Name
SQLFiddle Demo
You can use CASE statement for that: (This will work in MySQL)
SELECT p.name
,GROUP_CONCAT(CASE WHEN attr_id = 101 THEN value else NULL END) AS price
,GROUP_CONCAT(CASE WHEN attr_id = 102 THEN value else NULL END) AS color
,GROUP_CONCAT(CASE WHEN attr_id = 103 THEN value else NULL END) AS taste
FROM Products p JOIN Attributes a
ON p.id = a.prod_id
GROUP BY p.name;
Or you can also do the same with join: (This will work in both MySQL and SQL Server)
SELECT Name
,CASE WHEN a.attr_id = 101 THEN a.value ELSE NULL END AS price
,CASE WHEN b.attr_id = 102 THEN b.value else NULL END AS color
,CASE WHEN c.attr_id = 103 THEN c.value else NULL END AS taste
FROM Products p
LEFT JOIN Attributes a ON p.id = a.prod_id
LEFT JOIN Attributes b ON p.id = b.prod_id AND a.attr_id = 101 AND b.attr_id = 102
LEFT JOIN Attributes c ON p.id = c.prod_id AND a.attr_id = 101 AND c.attr_id = 103
WHERE a.attr_id IS NOT NULL and b.attr_id IS NOT NULL AND c.attr_id IS NOT NULL
I have also added a column for color.
Output
| NAME | PRICE | COLOR | TASTE |
-----------------------------------
| apple | 20 | red | sweet |
| orange | 30 | orange | sour |
| pear | 40 | green | sweet |
See this SQLFiddle
Demo for SQL Server
You have to query for each column separately...
SELECT
p.name,
(SELECT value FROM Attributes a WHERE attr_id=101 AND a.prod_ID=p.ID) AS price,
(SELECT value FROM Attributes a WHERE attr_id=102 AND a.prod_ID=p.ID) AS taste
FROM Products p
... or ...
If you are using MSSQL 2008 R2 you can use PIVOT:
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
Using PIVOT, easier to adjust for extra attributes.
SELECT NAME, [101],[102],[103]
FROM (
SELECT P.NAME as NAME, A.ATTR_ID as ATTR_ID, A.VALUE as VALUE
FROM PRODUCTS as P, ATTRIBUTES as A
WHERE A.PROD_ID = P.ID
) SrcTable
PIVOT
(
MAX(VALUE)
for ATTR_ID in ([101],[102],[103])
) PivotTable;
Or a dynamic statement could be generated to adjust for a variable number of attributes (since this is the only thing that changes in the above statement)
The column string could be obtained with somethings like:
DECLARE #columns AS NVARCHAR(MAX);
set #columns = STUFF((SELECT distinct ',' + QUOTENAME(ATTR_ID)
FROM ATTRIBUTES
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'');
Next, this could variable could be used to generate a string like the static PIVOT
DECALARE #stmt AS NVARCHAR(MAX);
set #stmt ='SELECT NAME, ' + #columns + '
FROM (
SELECT P.NAME as NAME, A.ATTR_ID as ATTR_ID, A.VALUE as VALUE
FROM PRODUCTS as P, ATTRIBUTES as A
WHERE A.PROD_ID = P.ID
) SourceTable
PIVOT (
MAX(VALUE)
for ATTR_ID in (' + #columns + ')
) PivotTable';
EXECUTE(#stmt);