MySQL 5.0 hierarchical recursive search - mysql

I have a table like this
CREATE TABLE IF NOT EXISTS `categories` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(150) NOT NULL,
`parent_id` INT(11) NULL,
`slug` VARCHAR(150) NOT NULL,
PRIMARY KEY (`id`),
)
This is an example of the table as how it is currently:
id name parent_id slug
----------------------------------------------------
1 Books NULL books
2 Anthology 1 anthology
3 Classic 1 classic
4 Drama 1 drama
5 Fable 1 fable
6 Aesop 5 aesop
7 Bidpai 5 bidpai
8 Stephen King 2 stephen-king
9 Magazines NULL magazines
10 Lifestyle 9 lifestyle
11 Wellness 9 wellness
12 Spa 11 spa
Note: i am on an old version of MySQL (5.0) so i cannot use recursive functions unfortunately
I want to select all parents and include them into the search result as well to get a nice overview of all the categories with the correct slugs in my application like this:
id name id_route slug
----------------------------------------------------
1 Books 1 books
2 Anthology 1,2 books/anthology
3 Classic 1,3 books/classic
4 Drama 1,4 books/drama
5 Fable 1,5 books/fable
6 Aesop 1,5,6 books/fable/aesop
7 Bidpai 1,5,7 books/fable/bidpai
8 Stephen King 1,2,8 books/anthology/stephen-king
9 Magazines 9 magazines
10 Lifestyle 9,10 magazines/lifestyle
11 Wellness 9,11 magazines/wellness
12 Spa 9,11,2 magazines/wellness/spa
Is it possible to retrieve a result like this in the first place? Because i am breaking my head around this at the moment. Any help or a push in the right direction would be amazing!

CREATE PROCEDURE GetTree()
BEGIN
CREATE TABLE tmp_cat LIKE categories;
ALTER TABLE tmp_cat ADD COLUMN path TEXT;
INSERT INTO tmp_cat (id, name, parent_id, slug, path)
SELECT id, name, parent_id, slug, name
FROM categories
WHERE parent_id IS NULL;
REPEAT
INSERT IGNORE INTO tmp_cat (id, name, parent_id, slug, path)
SELECT categories.id, categories.name, categories.parent_id, categories.slug, CONCAT(tmp_cat.path, ',', categories.name)
FROM categories
JOIN tmp_cat ON tmp_cat.id = categories.parent_id;
UNTIL ROW_COUNT() = 0
END REPEAT;
SELECT * FROM tmp_cat;
DROP TABLE tmp_cat;
END
fiddle

The following solution will work as far as you do NOT have more than three levels of hierarchy:
SELECT IF(level3_id IS NOT NULL, level3_id, IF(level2_id IS NOT NULL, level2_id, level1_id)) AS id,
IF(level3_name IS NOT NULL, level3_name, IF(level2_name IS NOT NULL, level2_name, level1_name)) AS name,
CONCAT_WS(",", level1_id, level2_id,level3_id) AS id_route,
CONCAT_WS("/", level1_slug, level2_slug,level3_slug) AS slug
FROM (
SELECT level1.id AS level1_id, level1.name AS level1_name, level1.slug AS level1_slug,
level2.id AS level2_id, level2.name AS level2_name, level2.slug AS level2_slug,
level3.id AS level3_id, level3.name AS level3_name, level3.slug AS level3_slug
FROM categories AS level1
RIGHT JOIN categories AS level2 ON level1.id = level2.parent_id
RIGHT JOIN categories AS level3 ON level2.id = level3.parent_id
) AS levels
ORDER BY id;
Sadly such solution will not scale up well. Recursive CTE is the better solution so you should consider upgrading.

Related

How to retrieve data solely by foreign key in MySQL?

The mission: Visualize the patients that were treated by surgeons.
Table: patients
id_patient
name
last_name
Table: doctors
id_doctor
name
last_name
speciality ("surgeon" is one of the possibilities)
Table: visits
id_visit
id_patient_fk (foreign key)
id_doctor_fk (foreign key)
With the id_doctor_fk pointing to id_doctor, I have to see if that doctor is a surgeon, right? Is this possible? I gave up, so I ask.
If you want patients who have seen a surgeon, you can use exists:
select p.*
from patients p
where exists (select 1
from visits v join
doctors d
on d.id_doctor = v.id_doctor_pk
where v.id_patient_fk = p.id_patient and
d.specialty = 'surgeon'
);
i created an example for your question . i hope this is help you:
CREATE TABLE #patients2 (
id_patient int,
[name] VARCHAR(100),
last_name VARCHAR(100)
);
CREATE TABLE #doctors2 (
id_doctor int,
[name] VARCHAR(100),
last_name VARCHAR(100),
speciality VARCHAR(100),
);
CREATE TABLE #dvisits2 (
id_visit int,
id_patient_fk int,
id_doctor_fk int
);
--drop table #table1
INSERT INTO #patients2
(id_patient, [name], last_name)
VALUES
(1,'patient1','last_name1'),
(2,'patient2','last_name2'),
(3,'patient3','last_name3'),
(4,'patient4','last_name4'),
(5,'patient5','last_name5'),
(6,'patient6','last_name6'),
(7,'patient7','last_name7'),
(8,'patient8','last_name8');
INSERT INTO #doctors2
(id_doctor, [name], last_name, speciality )
VALUES
(1,'doctor1','last_name1','surgeon'),
(2,'doctor2','last_name2','not surgeon'),
(3,'doctor3','last_name3','surgeon'),
(4,'doctor4','last_name4','not surgeon'),
(5,'doctor5','last_name5','surgeon'),
(6,'doctor6','last_name6','surgeon'),
(7,'doctor7','last_name7','not surgeon'),
(8,'doctor8','last_name8','surgeon');
INSERT INTO #dvisits2
(id_visit, id_doctor_fk,id_patient_fk)
VALUES
(1,1,1),
(2,2,2),
(3,3,3),
(4,4,4),
(5,5,5),
(6,6,6),
(7,7,7),
(8,8,8);
select * from #patients2 p
join #dvisits2 dv on p.id_patient = dv.id_patient_fk
join #doctors2 doc on dv.id_doctor_fk = doc.id_doctor
where doc.speciality = 'surgeon'
result =
id_patient [name] last_name id_visit id_patient_fk id_doctor_fk speciality id_doctor [name] last_name
1 patient1 last_name1 1 1 1 surgeon 1 doctor1 last_name1
1 patient1 last_name1 1 1 1 surgeon 1 doctor1 last_name1
3 patient3 last_name3 3 3 3 surgeon 3 doctor3 last_name3
3 patient3 last_name3 3 3 3 surgeon 3 doctor3 last_name3
5 patient5 last_name5 5 5 5 surgeon 5 doctor5 last_name5
6 patient6 last_name6 6 6 6 surgeon 6 doctor6 last_name6
8 patient8 last_name8 8 8 8 surgeon 8 doctor8 last_name8

MYSQL Count how many times product viewed in the same session

I am trying to create a "other people viewed these products" function.
I have a MySQL table which contains all of my page views:
Unique ID | Product ID | Session ID
1 5 ABCDREA
2 20 ABCDREA
3 9 REAVTAS
4 5 ESAGRRA
5 20 ESAGRRA
6 10 MVAOIRAS
7 6 MVAOIRAS
And so on.
I'm trying to populate a table that looks like the following:
Product ID | Also Viewed Product ID | Number of Times
5 20 2
20 5 2
10 6 1
6 10 1
So what I'm trying to do is first determine in my first table where a session ID appears more than once.
Secondly I'm then trying to then create an "association" between the multiple product ID's that appear in a session.
I'm happy to do this in multiple SQL statements. This will be executed by a perl script, so I can also manipulate data there too.
A good start would simply be able to generate a table that looked like this:
Session | Viewed Products
ABCD 1,52,512,6123
I could then work from there.
Any ideas?
Try the following:
select
session_id,
GROUP_CONCAT(Product_id) as viewed_products,
count(*) as count
From
yourtable
Group by
session_id
Using a test schema like
CREATE TABLE `t` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pid` int(11) DEFAULT NULL,
`sid` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `t` (`id`, `pid`, `sid`)
VALUES
(1,5,'abcdrea'),
(2,20,'abcdrea'),
(3,9,'reavtas'),
(4,5,'esagrra'),
(5,20,'esagrra'),
(6,10,'mva'),
(7,6,'mva');
You can get the first one with a simple JOIN:
select p1.sid, p1.pid as pid1, p2.pid as pid2
from t as p1
join t as p2 on (p1.sid = p2.sid and p1.pid <> p2.pid);
abcdrea 20 5
abcdrea 5 20
esagrra 20 5
esagrra 5 20
mva 6 10
mva 10 6
The second table is even easier:
select sid, group_concat(pid) from t group by sid;
abcdrea 5,20
esagrra 5,20
mva 10,6
reavtas 9

How to perform complex calculation and update to a view in MySQL?

I want to calculate how many Car_A and Car_B can be built with my existing items in warehouse?
Expecting to see the Car_A and Car_B with maximum number that I can build in the view below.
Please let me know if table design is bad or else.
What I have done: What I use everyday is export to Excel and calculate in Excel manually.
First I would suggest you to split the car and tool in the warehouse.
You can just create another column and put some sign for it like C = Car and T = Tools
Or you can create another table and put your car in it I prefer this way and my answer will goes this way and this is mydata test
CREATE TABLE [dbo].[Assembly](
[id_item] [int] NULL,
[name] [varchar](100) NULL,
[quantity] [int] NULL
)
insert into [Assembly]
select 1,'Car_A',0
union all
select 2,'Car_B',0
CREATE TABLE [dbo].[Warehouse](
[id_item] [int] NULL,
[name] [varchar](100) NULL,
[quantity] [numeric](18, 2) NULL
)
insert into [Warehouse]
select 1,'Door',30
union all
select 2,'Wheel',30
union all
select 3,'Light',30
CREATE TABLE [dbo].[Relation](
[assembly_id] [int] NULL,
[required_item] [int] NULL,
[required_quantity] [int] NULL
)
insert into [relation]
select 1,1,10
union all
select 1,2,10
union all
select 1,3,10
union all
select 2,1,3
union all
select 2,2,3
union all
select 2,3,3
testing the relation
select * from Assembly as a
inner join Relation as r on r.assembly_id = a.id_item
inner join Warehouse as w on w.id_item = r.required_item
The result is
id_item name quantity assembly_id required_item required_quantity id_item name quantity
1 Car_A 0 1 1 10 1 Door 30.00
1 Car_A 0 1 2 10 2 Wheel 30.00
1 Car_A 0 1 3 10 3 Light 30.00
2 Car_B 0 2 1 3 1 Door 30.00
2 Car_B 0 2 2 3 2 Wheel 30.00
2 Car_B 0 2 3 3 3 Light 30.00
and here your solution
select a.name,min(a.new_quantity) as new_quantity
from (
select a.name,floor(w.quantity/r.required_quantity) as new_quantity
from Assembly as a
inner join Relation as r on r.assembly_id = a.id_item
inner join Warehouse as w on w.id_item = r.required_item
) as a
group by a.name
name new_quantity
Car_A 3
Car_B 10
you can try editing your tools left to see if it's correct or not. Hope this help:)

List out column from one table, and two columns from another table

I just started learning Sql, so go easy on me. I'm trying to list out the item description for each toy, along with the customer names who bought them, as well as the dates of purchase.
Using aliases, I was able to get the name and the date_of_purchase using the following statement:
select tp.date_of_purchase, tp.name from table_a AS tp
left outer join table_b AS tt on tp.id = tt.id;
.. But I can't figure out how to get the item description from table_a as well. I need to get the name and date_of_purchase from tablea and the item_description from table_b using one sql statement. I've tried everything, but can't get it to work.
Here are the tables below:
table_a:
Column 1: id int(4) unsigned
Column 2: name varchar(100)
Column 3: quantity int(4) unsigned
Column 4: date_of_purchase date
50 John Miller 3 2013-03-16
25 Brad Cooper 1 2013-11-01
50 Alison Bradey 3 2013-11-01
50 Melissa Patterson 2 2014-02-25
75 Lisa Simpson 10 2015-08-15
table_b:
Column 1: id int(4) unsigned
Column 2: item_description varchar(100)
Column 3: quantity int(4) unsigned
Column 4: price decimal(5,2)
15 Super Nintendo 3 100.00
25 Action Figure 1 15.00
50 Gameboy 3 65.00
20 Nintendo 64 2 150.00
Any help would really be appreciated.
item_description is located in tableb(aka tt), so all you need to do is add that field (with a tt prefix since it's in tableb) to the select list;
SELECT tp.date_of_purchase, tp.name, tt.item_description
FROM table_a AS tp
LEFT OUTER JOIN tableb AS tt
ON tp.id = tt.id;
An SQLfiddle to test with.

MySQL: Group By & Count Multiple Fields: REDUX

OK, a slight variation on an earlier theme. Using the same basic idea, I want to get independent counts of the fields, then I want them grouped by a higher order breakdown.
I expanded the example by David to include a higher order column:
district_id, product_id, service_id
dist proj serv
1 1 1
1 1 2
1 1 2
1 1 3
1 1 3
1 1 4
1 2 2
1 2 4
1 2 4
1 2 5
1 2 5
2 1 1
2 2 1
2 1 6
2 2 6
2 3 6
To get a result on the total, I used a simple query with two sub-queries.
select
(select count(Distinct project_id) from GroupAndCountTest) AS "projects",
(select count(Distinct service_id) from GroupAndCountTest) as "services";
projects services
3 6
The challenge was to get this grouped within the district_id. What I wanted was:
district_id projects services
1 2 5
2 3 6
I ended up using similar sub-queries, but the only way I was able to combine them (other than using a stored function) was to re-run the sub-queries for every district. (Not a big problem here, but in my application the sub-queries use multiple tables with a substantial number of "districts" so the two sub-queries are run again for each "district" which will become increasingly ineffecient.
This query works, but I would love to see something more effecient.
select t1.district_id, p1.projects, s1.services
from GroupAndCountTest as t1
join (select district_id, count(Distinct project_id) as projects
from GroupAndCountTest
group by district_id) AS p1
on p1.district_id=t1.district_id
join (select district_id, count(Distinct service_id) as services
from GroupAndCountTest
group by district_id) as s1
on s1.district_id=t1.district_id
group by t1.district_id;
Thanks.
PS: If you want to experiment, you can create the table with:
CREATE TABLE `GroupAndCountTest` (
`district_id` int(5) DEFAULT NULL,
`project_id` int(5) DEFAULT NULL,
`service_id` int(5) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
insert into `GroupAndCountTest`(`district_id`,`project_id`,`service_id`)
values (1,1,1),(1,1,2),(1,1,2),(1,1,3),(1,1,3),(1,1,4),(1,2,2),(1,2,4),
(1,2,4),(1,2,5),(1,2,5),(2,1,1),(2,2,1),(2,1,6),(2,2,6),(2,3,6);
select district_id,
count(distinct(product_id)) projects,
count(distinct(service_id)) services
from MyTable group by district_id;
where MyTable contains district_id, product_id, service_id columns
You're making this way harder than it needs to be. You don't need subqueries for this, just a GROUP BY.
select district_id, count(distinct project_id), count(distinct service_id)
from GroupAndCountTest
group by district_id
SELECT district_id, count( DISTINCT (
project_id
) ) projects, count( DISTINCT (
service_id
) ) services
FROM GroupAndCountTest
GROUP BY district_id
I have been advanced :(