Sqlite select from multiple tables - sql

I have five tables such as
Base Table
| group_id | group_name |
|----------|-------------|
| 1 | gn1 |
| 2 | gn2 |
| 3 | gn3 |
"Tags" Table
| tag_id | tag_name |
|--------|----------|
| 1 | tgn1 |
| 2 | tgn2 |
| 3 | tgn3 |
"Theme" Table
| theme_id | theme_name |
|----------|------------|
| 1 | thn1 |
| 2 | thn2 |
| 3 | thn3 |
"Tags" Mapping Table
| rec_id | group_id | tag_id |
|--------|----------|--------|
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
"Theme" Mapping Table
| rec_id | group_id | theme_id |
|--------|----------|----------|
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 2 | 1 |
I am having some trouble creating a SQLite query to get a table like this:
| group_id | group_name | tags | themes |
|----------|------------|------------|------------|
| 1 | gn1 | tgn2, tgn3 | thn2 |
| 2 | gn2 | tgn1 | thn3, thn1 |
| 3 | gn3 | | |

The group_concat function will do the trick - join all the tables, group by the group id and name, and group_concat the other details:
SELECT g.group_id,
g.group_name,
GROUP_CONCAT(t.tag_name, ', ') AS tags
GROUP_CONCAT(th.theme_name, ', ') AS theme
FROM groups g
JOIN tags_map tg ON g.group_id = tm.group_id
JOIN tags t ON t.tag_id = tm.tag_id
JOIN themes_map thm ON g.group_id = thm.group_id
JOIN themes the ON th.theme_id = thm.theme_id
GROUP BY g.group_id, g.group_name

A pretty simple method uses correlated subqueries:
select b.*,
(select group_concat(t.tag_name)
from tag_mapping tm join
tags t
on tm.tag_id = t.tag_id
where tm.group_id = b.group_id
) as tags,
(select group_concat(t.theme_name)
from theme_mapping tm join
themes t
on tm.theme_id = t.theme_id
where tm.group_id = b.group_id
) as themes
from base b;

Related

SQL Server to display a view to show products at parent and child level

I have a table like this:
+----+----------+----------+-----------+
| ID | Name | Is_Group | Parent_id |
+----+----------+----------+-----------+
| 1 | Clothes | 1 | Null |
| 2 | Food | 1 | Null |
| 3 | fastfood | 1 | 2 |
| 4 | T-shirt | 0 | 1 |
| 5 | skirt | 0 | 1 |
| 6 | pizza | 0 | 3 |
| 7 | snack | 0 | 3 |
+----+----------+----------+-----------+
I would like to have a horizontal representation to use for reporting such as:
+----+---------+---------+----------+
| ID | Name | level1 | level2 |
+----+---------+---------+----------+
| 4 | T-shirt | Clothes | Null |
| 5 | skirt | Clothes | Null |
| 6 | pizza | Food | fastfood |
| 7 | snack | Food | fastfood |
+----+---------+---------+----------+
Would anyone know how to do this?
You can use two levels of left join:
select t.*,
coalesce(tpp.name, tp.name) as level1,
(case when tpp.name is not null then tp.name end) as level2
from t left join
t tp
on t.parent_id = tp.id left join
t tpp
on tp.parent_id = tpp.parent_id
where not exists (select 1
from t tc
where tc.parent_id = t.id);

How to handle multiple rows fulfilling criteria when joining Oracle tables

Given a table of roles, companies and a employee table where we store for each employee which role he/she has at each company.
I'm trying to create a view which indicates for each combination of role and company and employee by a ‘Y’ or ‘N’ in the “checked_yn” column, whether this employee has this role at this company.
company table
----------------
|ID | name |
-----------------
| 1 | A |
| 2 | B |
-----------------
roles table
-------------
|ID | role |
-------------
| 1 | X |
| 2 | Y |
| 3 | Z |
-------------
employee table
----------------------------------------------
|ID | company_id | role_id | employee_log_id |
---------------------------------------------|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | null | 1 |
----------------------------------------------
The desired outcome is this:
EMPLOYEE_ROLES_VW view
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | Y | 2 | 1 |
| 3 | 1 | 3 | N | null | 1 |
| 4 | 2 | 1 | N | null | 1 |
| 5 | 2 | 2 | N | null | 1 |
| 6 | 2 | 3 | N | null | 1 |
------------------------------------------------------------------------
This is my current query:
with ROLES_X_COMP as (SELECT ROL.ID AS X_ROLE_ID,
COM.ID AS X_COMPANY_ID,
FROM ROLES ROL
CROSS JOIN COMPANY COM)
SELECT ROWNUM AS ID,
EMP.ID AS SMCR_EMPLOYEE_ID,
EMP.EMPLOYEE_LOG_ID AS EMPLOYEE_LOG_ID,
ROLES_X_COMP.X_ROLE_ID ,
EMP.ROLE_ID AS ROLE_ID,
ROLES_X_COMP.X_COMPANY_ID,
EMP.COMPANY_ID AS COMPANY_ID,
CASE
WHEN ROLES_X_COMP.X_ROLE_ID = SE.ROLE_ID AND ROLES_X_COMP.X_COMPANY_ID =
SE.COMPANY_ID THEN 'Y'
ELSE 'N' END AS CHECKED_YN
FROM ROLES_X_COMP
LEFT OUTER JOIN EMPLOYEE EMP ON ROLES_X_COMP.X_COMPANY_ID = EMP.COMPANY_ID
Because of the join on EMPLOYEE “finds” the company with id=1 twice it joins twice with the cross join of role and company table. So I'm getting this result:
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | N | 1 | 1 |
| 3 | 1 | 3 | N | 1 | 1 |
| 4 | 1 | 1 | N | 2 | 1 |
| 5 | 1 | 2 | Y | 2 | 1 |
| 6 | 1 | 3 | N | 2 | 1 |
| 7 | 2 | 1 | N | 3 | 1 |
| 8 | 2 | 2 | N | 3 | 1 |
| 9 | 2 | 3 | N | 3 | 1 |
------------------------------------------------------------------------
I think a JOIN might be the wrong option here and a UNION more appropriate but I can't figure it out.
Use a partitioned outer join:
Query:
SELECT ROWNUM AS id,
e.company_id,
r.id AS role_id,
NVL2( e.role_id, 'Y', 'N' ) AS CheckedYN,
e.role_id AS employee_id,
e.employee_log_id
FROM roles r
LEFT OUTER JOIN
employee e
PARTITION BY ( e.company_id, e.employee_log_id )
ON ( r.id = e.role_id )
or (depending on how you want to partition and join the data):
SELECT ROWNUM AS id,
c.id AS company_id,
r.id AS role_id,
NVL2( e.role_id, 'Y', 'N' ) AS CheckedYN,
e.role_id AS employee_id,
e.employee_log_id
FROM roles r
CROSS JOIN
company c
LEFT OUTER JOIN
employee e
PARTITION BY ( e.employee_log_id )
ON ( c.id = e.company_id AND r.id = e.role_id )
Output:
Both output the same for the test data but may give differing results depending on your actual data.
ID | COMPANY_ID | ROLE_ID | CHECKEDYN | EMPLOYEE_ID | EMPLOYEE_LOG_ID
-: | ---------: | ------: | :-------- | ----------: | --------------:
1 | 1 | 1 | Y | 1 | 1
2 | 1 | 2 | Y | 2 | 1
3 | 1 | 3 | N | null | 1
4 | 2 | 1 | N | null | 1
5 | 2 | 2 | N | null | 1
6 | 2 | 3 | N | null | 1
db<>fiddle here
AND ROLES_X_COMP.X_ROLE_ID = EMP.ROLE_ID
Is missing at the end of your query
But the outcome will be
EMPLOYEE_ROLES_VW view
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | Y | 2 | 1 |
| 3 | 1 | 3 | N | null | null |
| 4 | 2 | 1 | N | null | null |
| 5 | 2 | 2 | N | null | null |
| 6 | 2 | 3 | N | null | null |
------------------------------------------------------------------------

Join two tables. Select all rows from one table and only matching values from other table?

I have the following tables:
Locations
+-------------+----------------+
| Location_ID | Location_Name |
+-------------+----------------+
| 1 | Administration |
| 2 | Parking |
| 3 | Warehouse |
| 4 | Shipping |
| 5 | Factory |
| 6 | Office |
| 7 | Processing |
+-------------+----------------+
Item_Quantity
+---------+-------------+-------------------+
| Item_ID | Location_ID | Location_Quantity |
+---------+-------------+-------------------+
| 1 | 3 | 10 |
| 1 | 5 | 50 |
| 2 | 3 | 7 |
+---------+-------------+-------------------+
I am trying to get a list of all Location_IDs and Location_Names with the Location_Quantity for a specified Item_ID.
The expected result for Item_ID = 1 would be this:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 1 | Administration | 0 |
| 2 | Parking | 0 |
| 3 | Warehouse | 10 |
| 4 | Shipping | 0 |
| 5 | Factory | 50 |
| 6 | Office | 0 |
| 7 | Processing | 0 |
+-------------+----------------+-------------------+
The expected result for Item_ID = 2 would be this:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 1 | Administration | 0 |
| 2 | Parking | 0 |
| 3 | Warehouse | 7 |
| 4 | Shipping | 0 |
| 5 | Factory | 0 |
| 6 | Office | 0 |
| 7 | Processing | 0 |
+-------------+----------------+-------------------+
I have tried the following queries:
SELECT l.Location_ID, l.Location_Name, iq.Location_Quantity
FROM Locations l
LEFT JOIN Item_Quantity iq ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
SELECT l.Location_ID, l.Location_Name, iq.Location_Quantity
FROM Item_Quantity iq
LEFT JOIN Locations l ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
SELECT l.Location_ID, l.Location_Name, Location_Quantity = iif(iq.Location_Quantity IS NOT NULL, iq.Location_Quantity, 0)
FROM Locations l
LEFT JOIN Item_Quantity iq ON l.Location_ID = iq.Location_ID
WHERE iq.Item_ID = #Item_ID
All queries return only the rows with entries in Item_Quantity.
This is what I am getting for Item_ID = 1 for any of the above queries:
+-------------+----------------+-------------------+
| Location_ID | Location_Name | Location_Quantity |
+-------------+----------------+-------------------+
| 3 | Warehouse | 10 |
| 5 | Factory | 50 |
+-------------+----------------+-------------------+
I would have thought a Left Join on the Locations table would give me all of the rows from the specified columns, but I must be understanding something incorrectly?
Can anyone see what I am doing wrong here?
The condition needs to go in the ON clause. Otherwise, the WHERE clause turns the outer join into an inner join.
You also want to convert the NULL to a 0, so use COALESCE():
SELECT l.Location_ID, l.Location_Name, COALESCE(iq.Location_Quantity, 0) as Location_Quantity
FROM Locations l LEFT JOIN
Item_Quantity iq
ON l.Location_ID = iq.Location_ID AND iq.Item_ID = #Item_ID;

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

Finding number of types of accounts from each customer

I am having a lot of trouble with trying to construct a query that will give me the name of each customer and the number of different types of accounts each has. The three types are Checkings, Savings, and CD.
customers:
+--------+--------+
| cid | name |
+--------+--------+
| 1 | a |
| 2 | b |
| 3 | c |
+--------+--------+
accounts:
+-----------+-----------+
| aid | type |
+-----------+-----------+
| 1 | Checkings |
| 2 | Savings |
| 3 | Checkings |
| 4 | CD |
| 5 | CD |
| 6 | Checkings |
+-----------+-----------+
transactions:
+--------+--------+--------+
| tid | cid | aid |
+--------+--------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 4 |
| 7 | 1 | 6 |
+--------+--------+--------+
The expected answer would be:
a, 3
b, 1
c, 1
Getting the names is simple enough, but how can I keep count of each individual's account as well as compare the accounts to make sure that it is not the same type?
just add DISTINCT inside the COUNT
SELECT a.cid, a.name, COUNT(DISTINCT c.type) totalCount
FROM customers a
INNER JOIN transactions b
ON a.cis = b.cid
INNER JOIN accounts c
ON b,aid = c.aid
GROUP BY a.cid, a.name
Query:
SQLFiddleExample
SELECT
a."name",
COUNT(DISTINCT c."type") totalCount
FROM customers a
INNER JOIN transactions b
ON a."cid" = b."cid"
INNER JOIN accounts c
ON b."aid" = c."aid"
GROUP BY a."cid", a."name"
ORDER BY totalCount DESC
Result:
| NAME | TOTALCOUNT |
---------------------
| a | 3 |
| b | 1 |
| c | 1 |