View Hierarchical data as columns in sql - mysql

I have a self referencing table to store a hierarchy.
vehicle_id
vehicle_name
parent_vehicle_id
1
social_media
NULL
2
facebook
1
3
instagram
1
4
stories
2
5
reel
3
6
igtv
3
using a cte, I am able to get the vehicle levels like so
with vehicle_tree (vehicle_id, vehicle_name, parent_vehicle_id, veh_level)
as
(
select v.vehicle_id, v.vehicle_name, v.parent_vehicle_id, 1 as veh_level from vehicle as v where v.parent_vehicle_id is null
union all
select sub_veh.vehicle_id, sub_veh.vehicle_name, sub_veh.parent_vehicle_id, vt.veh_level + 1 from vehicle as sub_veh
inner join vehicle_tree as vt
on vt.vehicle_id = sub_veh.parent_vehicle_id
where sub_veh.parent_vehicle_id is not null
)
select * from vehicle_tree
with the output
vehicle_id
vehicle_name
parent_vehicle_id
veh_level
1
social_media
NULL
1
2
facebook
1
2
3
instagram
1
2
5
reel
3
3
6
igtv
3
3
4
stories
2
3
What I would like to have, is to view the hierarchies as columns rather than rows something like this
1_id
1
2_id
2
3_id
3
1
social_media
2
facebook
4
stories
1
social_media
3
instagram
5
reel
1
social_media
3
instagram
6
igtv
I have gotten as far as this
with vehicle_tree (vehicle_id, vehicle_name, parent_vehicle_id, veh_level)
as
(
select v.vehicle_id, v.vehicle_name, v.parent_vehicle_id, 1 as veh_level from vehicle as v where v.parent_vehicle_id is null
union all
select sub_veh.vehicle_id, sub_veh.vehicle_name, sub_veh.parent_vehicle_id, vt.veh_level + 1 from vehicle as sub_veh
inner join vehicle_tree as vt
on vt.vehicle_id = sub_veh.parent_vehicle_id
where sub_veh.parent_vehicle_id is not null
)
select * from vehicle_tree l1
left outer join vehicle_tree l2 on l2.veh_level = 2 and l1.vehicle_id = l2.parent_vehicle_id
left outer join vehicle_tree l3 on l3.veh_level = 3 and l2.vehicle_id = l3.parent_vehicle_id
where l1.veh_level = 1
which gives the output that I expect but I would like to have some form of a recursive solution on the left outer joins instead
vehicle_id
vehicle_name
parent_vehicle_id
veh_level
vehicle_id
vehicle_name
parent_vehicle_id
veh_level
vehicle_id
vehicle_name
parent_vehicle_id
veh_level
1
social_media
NULL
1
2
facebook
1
2
4
stories
2
3
1
social_media
NULL
1
3
instagram
1
2
5
reel
3
3
1
social_media
NULL
1
3
instagram
1
2
6
igtv
3
3
Kindly advise how I can achieve the same output via a recursive function. I would prefer a solution that could be used on any engine but MS SQL will suffice for now

Because a SQL query returns a fixed set of columns, you can just use explicit JOINs to get the names in different columns:
select vt.*, vt2.*, vt3.*
from vehicle_tree vt left join
vehicle_tree vt2
on vt2.parent_vehicle_id = vt.vehicle_id left join
vehicle_tree vt3
on vt3.parent_vehicle_id = vt2.vehicle_id left join
where vt.parent_vehicle_id is null

Related

How to Combine GROUP CONCAT with COUNT in MySQL?

I am trying to write a query that can get result from multiple tables:
Item Category
ic_id
ic_name
1
PC
2
Laptop
3
Printer
4
Scanner
Items
i_id
i_category
i_name
1
1
Dell Optiplex
2
2
HP Probook 450
3
2
HP Probook650
4
3
HP Laserjet 402dn
5
1
Dell MT3030
Item Sale
is_id
is_date
is_customer
1
15-03-2021
John
2
16-03-2022
Jimmy
3
18-03-2023
Mark
Item Sale Detail
isd_id
isd_sale_id
isd_item
1
1
2
2
1
3
3
2
4
4
3
1
5
3
5
6
3
4
Is it possible to get combined result of GROUP CONCAT with COUNT in 1 query? Please guide me to write the query to get the desired result, I want the query result as shown below, Thanks:
Desired Result
is_id
is_date
is_customer
items
1
15-03-2021
John
Laptop: 3
2
16-03-2022
Jimmy
Printer: 1
3
18-03-2023
Mark
PC:2 , Printer: 1
If I understand correctly, this is just join but with two levels of aggregation:
select its.*, ic.categories
from item_sale its join
(select isd.isd_sale_id,
group_concat(ic_name, ':', cnt order by cnt desc) as categories
from (select isd.isd_sale_id, ic.ic_name, count(*) as cnt
from item_sale_detail isd join
items i
on isd.isd_item = i.i_id join
item_category ic
on i.i_category = ic.ic_id
group by isd.isd_sale_id, ic.ic_name
) ic
group by isd.isd_sale_id
) ic
on i.is_id = ic.isd_sale_id;

Finding all or none

I have four tables detailing an amusement park and its guests' ride history.
Categories
c_id name
1 Thrill
2 Leisure
3 Kiddie
Rides
r_id c_id
1 1
2 1
3 2
4 2
5 3
6 3
guest_history
h_id g_id
1 1
2 1
3 2
4 3
history_items
h_id r_id
1 5
2 6
3 1
3 2
4 5
How would I get all of the guests (g_id's) that have either rode all of the kiddie rides or none of the kiddie rides?
Expected Output would be:
g_id
1
2
I can't seem to figure out what the easiest way to go about it would be. I can only seem to conjure up a table that contains all of the cases that a guest has rode a kiddie ride. My attempt was a 4-way inner join of the tables and to filter out on the c_id = "Kiddie". Any help would be appreciated.
You can try this below logic-
3 is fixed in the query to get category "Kiddie"
DEMO HERE
SELECT A.g_id,COUNT(C.r_id)
FROM guest_history A
INNER JOIN history_items B ON A.h_id = B.h_id
INNER JOIN Rides C ON B.r_id = C.r_id AND C.c_id = 3
GROUP BY A.g_id
HAVING COUNT(DISTINCT B.r_id) = (SELECT COUNT(r_id) FROM Rides WHERE c_id = 3)
OR COUNT(DISTINCT B.r_id) = 0
SELECT g_id
FROM Categories
NATURAL JOIN Rides
NATURAL JOIN guest_history
NATURAL JOIN history_items
GROUP BY g_id
HAVING COUNT(DISTINCT r_id) IN (0,
(SELECT COUNT(DISTINCT r_id)
FROM Categories
NATURAL JOIN Rides
WHERE name = 'Kiddie')
)
fiddle

Get most sold products with a specified one

I have a table order_detail with this informations
id_order id_product
1 2
1 3
2 2
2 4
2 5
2 3
3 2
3 1
4 2
4 3
4 1
4 6
I would like to get most sold products(the first 5 elements) with the current product let's say product id 2
I tried this but it returns one wrong result
SELECT od2.product_id, count(od2.`product_id`) FROM `ps_order_detail` od1
LEFT JOIN ps_order_detail od2 ON od1.id_order = od2.id_order where
od2.product_id != od1.product_id AND od1.product_id=2
The result should be
product_id count(od2.`product_id`)
3 3
4 2
1 1
5 1
6 1
Your query is on the right track. Mostly, you are missing a group by:
select od.product_id, count(od2.id_order) as NumTimesWith2
from ps_order_detail od left join
ps_order_detail od2
on od.id_order = od2.id_order and
od2.product_id = 2
where od.product_id <> 2
group by od.product_id
order by count(od2.id_order) desc;
If you want only one such product, then add a limit 1 to the query.
Also, this assumes that products are not repeated inside orders. If they can be, you can quickly get a better count using count(distinct od.id_order)).

How to join two tables with two conditions?

I have two tables:
LLOAN
LOANID SOURCEID LOAN_COMPANY ETC
1 1 3
2 1 3
3 1 1
4 2 1
5 2 1
6 2 1
7 3 1
8 3 1
COMPANY
CompanyID CountryID CompanyIDLLAS
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
6 4 1
And I want to join them. The SourceID refers to the CountryID and the LOAN_COMPANY refers to the CompanyID. Only country '1' has multiple companies, all the others just have one.
How can I join these two tables correctly? I've tried many different things, of which this came the closest:
SELECT Count(c.CompanyID) FROM dbo.LLOAN As l
LEFT JOIN dbo.Company As c ON c.CountryID = l.SourceID AND c.CompanyID = l.LOAN_COMPANY
But it leaves many rows blank. What is the correct way to join two tables with two conditions?
Try below Query:
SELECT Count(c.CompanyID)
FROM dbo.LLOAN As LL
LEFT JOIN dbo.Company As C
ON (C.CountryID = LL.SourceID)
AND (C.CompanyID = LL.LOAN_COMPANY)
You can group the condition using paranthesis like this:
SELECT Count(c.CompanyID)
FROM dbo.LLOAN As l
LEFT JOIN dbo.Company As c ON (c.CountryID = l.SourceID) AND (c.CompanyID = l.LOAN_COMPANY)

Mysql Grouping and Counting

i have a 3 Tables:
1 TableTraders
trader_id|Domain|
---------------------
1|google.com
2|yahoo.com
2 Table Counter_in
counter_time counter_ip counter_domain_id
111111 222222 1
111111 222224 2
111111 222225 3
111111 222232 2
111111 222221 3
111111 222223 4
1111311 22223422 5
3 Table Out Counter
counter_time counter_ip counter_domain_id
111111 222222 1
111111 222222 2
111111 222222 3
111111 222226 2
111111 222221 3
111111 222222 4
1309351485 2130708581 5
1309351485 2130708581 4
1309710116 2130706433 4
1309351485 2130708581 1
1309710274 2130706433 1
Ok what i want todo is join the tables 1 , 2 , 3 and group them together, and count them.
trader_id|DOMAIN|IN|OUT
--------------------------
1|google.com|3|1
2|yahoo.com|1|2
I tried many things, but i get always wrong results. How do i join right the queries that i get results i want
*EDIT
The query attempted:
SELECT traders.trader_id, traders.domain, COUNT( counter_in.counter_domain_id ) AS today_in, COUNT( counter_out.counter_domain_id ) AS today_out
FROM traders
JOIN counter_in ON traders.trader_id = counter_in.counter_domain_id
JOIN counter_out ON traders.trader_id = counter_out.counter_domain_id
GROUP BY traders.trader_id, traders.domain
LIMIT 0 , 30
Results:
trader_id domain today_in today_out
1 bing.com 3 3
2 google.com 4 4
3 yahoo.com 4 4
4 msn.com 3 3
5 yandex.com 1 1
SELECT
TableTraders.trader_id,
TableTraders.Domain,
COUNT(TableIn.trader_id) AS in_count,
COUNT(TableOut.trader_id) AS out_count
FROM TableTraders
JOIN TableIn ON TableTraders.trader_id = TableIn.trader_id
JOIN TableOut ON TableTraders.trader_id = TableOut.trader_id
GROUP BY TableTraders.trader_id, TableTraders.Domain
Not certain what your table names are from your example above, since they contain spaces. Substitute the correct table names.
It appears the other answer may be giving you some sort of Cartesian result thus duplicating results. Take your traders and join to distinct sub-query counts from respective in / out tables.
SELECT
TT.trader_id,
TT.Domain,
TIN.InCount,
TOUT.OutCount
FROM
TableTraders TT
LEFT Join ( select Trader_ID, count(*) as InCount
from TableIn
group by Trader_ID ) TIN
on TT.Trader_ID = TIN.Trader_ID
LEFT Join ( select Trader_ID, count(*) as InCount
from TableOut
group by Trader_ID ) TOUT
on TT.Trader_ID = TOUT.Trader_ID
If you want it by DISTINCT IP address per in/out, just change
COUNT(*)
to
COUNT( Distinct IP )