Mysql - Add row as column - mysql

I am having a table called listings. A listing can have 2 images, a front and a back-image.
I currently join the tables and get the following output:
**Schema (MySQL v5.7)**
CREATE TABLE `images` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`listings_id` int(11) DEFAULT NULL,
`file_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`properties` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `listings` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`price` double(8,2) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO images (id, listings_id, file_name, properties, created_at, updated_at) VALUES(1, 1, 'trv_front.png', '[{"front_back":"front","path":null}]', '2021-10-16 07:08:06', '2021-10-16 07:08:06');
INSERT INTO images (id, listings_id, file_name, properties, created_at, updated_at) VALUES(2, 2, '20211016070806_trv.png', '[{"front_back":"front","path":null}]', '2021-10-16 07:08:06', '2021-10-16 07:08:06');
INSERT INTO images (id, listings_id, file_name, properties, created_at, updated_at) VALUES(3, 1, '2_rv.png', '[{"front_back":"back","path":null}]','2021-10-16 07:08:06', '2021-10-16 07:08:06');
INSERT INTO images (id, listings_id, file_name, properties, created_at, updated_at) VALUES(4, 2, 'backd.png', '[{"front_back":"back","path":null}]','2021-10-16 07:08:06', '2021-10-16 07:08:06');
INSERT INTO listings (id, title, price, created_at, updated_at) VALUES(1, 'test1', 10, '2021-10-16 08:14:08', '2021-10-16 08:14:08');
INSERT INTO listings (id, title, price, created_at, updated_at) VALUES(2, 'test2', 42, '2021-10-16 08:14:08', '2021-10-16 08:14:08');
INSERT INTO listings (id, title, price, created_at, updated_at) VALUES(3, 'test3', 43, '2021-10-16 08:14:08', '2021-10-16 08:14:08');
INSERT INTO listings (id, title, price, created_at, updated_at) VALUES(4, 'test4', 50, '2021-10-16 08:14:08', '2021-10-16 08:14:08');
---
**Query #1**
select l.id, l.title, i.file_name, i.listings_id, i.properties from listings l
left join images i on i.listings_id = l.id order by l.id;
| id | title | file_name | listings_id | properties |
| --- | ----- | ---------------------- | ----------- | ------------------------------------ |
| 1 | test1 | trv_front.png | 1 | [{"front_back":"front","path":null}] |
| 1 | test1 | 2_rv.png | 1 | [{"front_back":"back","path":null}] |
| 2 | test2 | 20211016070806_trv.png | 2 | [{"front_back":"front","path":null}] |
| 2 | test2 | backd.png | 2 | [{"front_back":"back","path":null}] |
| 3 | test3 | | | |
| 4 | test4 | | | |
As you can see in the properties-column there I can see if the image is front or the back side.
However, I would like to get the following structure adding the two columns front_image and back_image:
| id | title | file_name | listings_id | front_image | back_image |
|----|-------|------------------------|-------------|-------------------------|------------|
| 1 | test1 | trv_front.png | 1 | trv_front.png | 2_rv.png |
| 2 | test2 | 20211016070806_trv.png | 2 | 220211016070806_trv.png | backd.png |
| 3 | test3 | | | | |
| 4 | test4 | | | | |
This is my dbfiddle.
Any suggestions how to add the image columns?
I appreciate your replies!

Try this:
Query #1
SELECT
Id,
Title,
File_Name,
listings_id,
MIN(front_image),
MIN(back_image)
FROM
(
SELECT
l.id,
l.title,
i.file_name,
i.listings_id,
CASE
WHEN i.properties = '[{"front_back":"front","path":null}]' THEN i.file_name
END AS front_image,
CASE
WHEN i.properties = '[{"front_back":"back","path":null}]' THEN i.file_name
END AS back_image,
i.properties FROM listings l
LEFT JOIN images i ON i.listings_id = l.id
ORDER BY l.id
) AS t1 GROUP BY id;
id
title
file_name
listings_id
MIN(front_image)
MIN(back_image)
1
test1
trv_front.png
1
trv_front.png
2_rv.png
2
test2
20211016070806_trv.png
2
20211016070806_trv.png
backd.png
3
test3
4
test4
View on DB Fiddle

Please use the case in your sql query :
Example is given below.
select l.id, l.title, i.file_name, i.listings_id,
CASE
WHEN i.properties = '[{"front_back":"front","path":null}]' THEN i.file_name
END AS front_image,
CASE
WHEN i.properties = '[{"front_back":"back","path":null}]' THEN i.file_name
END AS back_image,
i.properties from listings l
left join images i on i.listings_id = l.id order by l.id

Make use of JSON function in MySQL, JSON_EXTRACT(json_doc, path[, path] ...)
Returns data from a JSON document, selected from the parts of the document matched by the path arguments. Returns NULL if any argument is NULL or no paths locate a value in the document. An error occurs if the json_doc argument is not a valid JSON document or any path argument is not a valid path expression.
SELECT
a.id,
MAX(title) AS title,
GROUP_CONCAT(b.file_name) AS file_names,
MAX(b.listings_id) AS listings_id,
MAX(CASE WHEN JSON_EXTRACT(b.properties, '$[0].front_back') = 'front' THEN b.file_name ELSE NULL END) AS front_image,
MAX(CASE WHEN JSON_EXTRACT(b.properties, '$[0].front_back') = 'back' THEN b.file_name ELSE NULL END) AS back_image
FROM listings a
LEFT JOIN images b ON a.id = b.listings_id
GROUP BY a.id

Related

Mysql returning multiple row instead of one row with array of Images

Consider the following schema:
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(45) NULL,
PRIMARY KEY (id)
);
CREATE TABLE attachments (
id INT NOT NULL,
name VARCHAR(45) NULL DEFAULT NULL,
filePath VARCHAR(255) NULL DEFAULT NULL,
thumbnailPath VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (id)
);
CREATE TABLE reviews (
id INT NOT NULL AUTO_INCREMENT,
productId INT NULL,
user_id INT NULL,
review VARCHAR(45) NULL,
rating INT NULL,
PRIMARY KEY (id));
CREATE TABLE review_attachments (
id INT NOT NULL AUTO_INCREMENT,
review_id INT NULL,
attachment_id INT NULL,
PRIMARY KEY (id));
INSERT INTO users (id, name) VALUES
('1', 'XYZ'),
('2', 'ABC'),
('3', 'EVE');
INSERT INTO attachments (id, name, filePath, thumbnailPath) VALUES
('100', 'f.png', '/resources/attachments/2020/f.png', '/resources/attachments/thumbnail/2020/f.png'),
('101', 'd.png', '/resources/attachments/2020/d.png', '/resources/attachments/thumbnail/2020/d.png'),
('102', 'g.png', '/resources/attachments/2020/g.png', '/resources/attachments/thumbnail/2020/g.png');
INSERT INTO reviews (id, productId, user_id, review, rating) VALUES
('1', '1', '1', 'Great Product', '5'),
('2', '1', '2', 'Good Product', '4'),
('3', '1', '3', 'Bad Product', '1');
INSERT INTO review_attachments (id, review_id, attachment_id) VALUES
('1', '1', '100'),
('2', '1', '101'),
('3', '1', '102');
When I query against this schema using the following query I am getting two rows with the same entries.
I am using JSON_ARRAYAGG to return an array it may be because of groupby.
Can anyone help me get this query right?
select
review, rating, u.name,reviews.createdAt,
(SELECT
JSON_ARRAYAGG(JSON_OBJECT("attachmentId", attachments.id,
'filePath', TRIM(CONCAT("${process.env.NODE_SERVER_API_HOST}", '/',attachments.filePath)),
'thumbnailPath', TRIM(CONCAT("${process.env.NODE_SERVER_API_HOST}", '/',attachments.thumbnailPath))
))
FROM attachments
WHERE attachments.id = ra.attachment_id) as reviewAttachments
from reviews
left join users as u on u.id = reviews.user_id
left join reviewAttachments as ra on ra.review_id = reviews.id
left join attachments as at on at.id = ra.attachment_id
where productId = 1
and reviews.isDeleted =0
and reviews.review is not null
limit 0,5
|review |rating| name | createdAt | reviewAttachments |
|:---- |:----:| -----:| ---------:| ----------------:
|Great Product | 5 | XYZ | 2020-11-04| [{"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/f.png", "attachmentId": 102, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/f.png"}]
|Great Product | 5 | XYZ | 2020-11-04| [{"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/d.png", "attachmentId": 101, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/d.png"}]
|Great Product | 5 | XYZ | 2020-11-04| [{"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/g.png", "attachmentId": 100, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/g.png"}]
|Good Product | 4 | ABC | 2020-11-04| null
|Bad Product | 1 | EVE | 2020-11-04| null
Desired output:
|review |rating| name | createdAt | reviewAttachments |
|:---- |:----:| -----:| ---------:| ----------------:
|Great Product | 5 | XYZ | 2020-11-04| [{"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/f.png", "attachmentId": 102, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/f.png"}, {"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/d.png", "attachmentId": 101, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/d.png"}, {"filePath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/2020/g.png", "attachmentId": 100, "thumbnailPath": "${process.env.NODE_SERVER_API_HOST}/resources/attachments/thumbnail/2020/g.png"}]
|Good Product | 4 | ABC | 2020-11-04| null
|Bad Product | 1 | EVE | 2020-11-04| null
Notice: The above row with rating 5 is returning array of reviewAttachments associated with that review. My output is returning each row with duplicate entries of that rating with different review attachment.
select
review, rating, u.name,
(SELECT
JSON_ARRAYAGG(JSON_OBJECT("attachmentId", attachments.id,
'filePath', TRIM(CONCAT("${process.env.NODE_SERVER_API_HOST}", '/',attachments.filePath)),
'thumbnailPath', TRIM(CONCAT("${process.env.NODE_SERVER_API_HOST}", '/',attachments.thumbnailPath))
))
FROM review_attachments ra
left join attachments on attachments.id = ra.attachment_id
WHERE ra.review_id = reviews.id) as reviewAttachments
from reviews
left join users as u on u.id = reviews.user_id
where productId = 1
and reviews.review is not null
limit 0,5

MySql: how to get the desired result

I've a table like this:
CREATE TABLE `base_build_floor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`build_no` varchar(64) NOT NULL,
`build_name` varchar(64) DEFAULT NULL,
`floor_no` varchar(64) DEFAULT NULL,
`floor_name` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
)
and insert some data:
INSERT INTO `base_build_floor` VALUES ('41', 'BUILD40210011', 'A', null, null);
INSERT INTO `base_build_floor` VALUES ('42', 'BUILD40210012', 'B', null, null);
INSERT INTO `base_build_floor` VALUES ('43', 'BUILD40210013', 'C', null, null);
INSERT INTO `base_build_floor` VALUES ('44', 'BUILD40210013', 'C', 'FLOOR40210002', 'C1');
INSERT INTO `base_build_floor` VALUES ('45', 'BUILD40210013', 'C', 'FLOOR40210003', 'C2');
INSERT INTO `base_build_floor` VALUES ('46', 'BUILD40210012', 'B', 'FLOOR40210004', 'B1');
the table is about a build-floor table, first you should make a building, then, a building can has no or some floors. the A building has no floor, the B building has one floor named B1, the C building has two floors named C1 and C2, I want to get the result as below:
41 BUILD40210011 A null null
44 BUILD40210013 C FLOOR40210002 C1
45 BUILD40210013 C FLOOR40210003 C2
46 BUILD40210012 B FLOOR40210004 B1
it means that, if a building has no floors, then get it, while if a building has any one floor, the building itself should not be got, so how to write the mysql?I've tried to use Subquery but doesn't work
I've try like this :
SELECT
b.*
FROM
base_build_floor b
WHERE
b.floor_no IS NOT NULL
OR (
b.floor_no IS NULL
AND b.build_no NOT IN (
SELECT
GROUP_CONCAT(nostr)
FROM
(
SELECT
concat("'", f.build_no, "'") as nostr
FROM
base_build_floor f
WHERE
f.floor_no IS NOT NULL
GROUP BY
f.build_no
) t
)
)
but I get all the data
With NOT EXISTS:
select t.* from base_build_floor t
where t.floor_no is not null
or not exists (
select 1 from base_build_floor
where build_no = t.build_no and floor_no is not null
)
See the demo.
Results:
| id | build_no | build_name | floor_no | floor_name |
| --- | ------------- | ---------- | ------------- | ---------- |
| 41 | BUILD40210011 | A | | |
| 44 | BUILD40210013 | C | FLOOR40210002 | C1 |
| 45 | BUILD40210013 | C | FLOOR40210003 | C2 |
| 46 | BUILD40210012 | B | FLOOR40210004 | B1 |
This query would be much simpler if you had normalized tables. Ideally, you would have a buildings table with building id, no, and name, and a floors table with building id, floor no, and floor name. Then you could just join the two tables. Since that's not the case, we can basically extract the building and floor sub-tables from the main one and join them like this:
SELECT
b.build_no,
b.build_name,
f.floor_no,
f.floor_name
FROM
(SELECT DISTINCT build_no, build_name
FROM base_build_floor) b
LEFT OUTER JOIN
(SELECT *
FROM base_build_floor
WHERE floor_no IS NOT NULL) f ON b.build_no = f.build_no

GROUP BY + HAVING ignore row

Basically what I wanted is that I can select all the race records with record holder and best time. I looked up about similar queries and managed to find 3 queries that were faster than the rest.
The problem is it completely ignores the race the userid 2 owns the record of.
These are my tables, indexes, and some sample data:
CREATE TABLE `races` (
`raceid` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`raceid`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`userid` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `race_times` (
`raceid` smallint(5) unsigned NOT NULL,
`userid` mediumint(8) unsigned NOT NULL,
`time` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`raceid`,`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `races` (`raceid`, `name`) VALUES
(1, 'Doherty'),
(3, 'Easter Basin Naval S'),
(5, 'Flint County'),
(6, 'Fort Carson'),
(4, 'Glen Park'),
(2, 'Palomino Creek'),
(7, 'Tierra Robada');
INSERT INTO `users` (`userid`, `name`) VALUES
(1, 'Player 1'),
(2, 'Player 2');
INSERT INTO `race_times` (`raceid`, `userid`, `time`) VALUES
(1, 1, 51637),
(1, 2, 50000),
(2, 1, 148039),
(3, 1, 120516),
(3, 2, 124773),
(4, 1, 101109),
(6, 1, 89092),
(6, 2, 89557),
(7, 1, 77933),
(7, 2, 78038);
So if I run these 2 queries:
SELECT rt1.raceid, r.name, rt1.userid, p.name, rt1.time
FROM race_times rt1
LEFT JOIN users p ON (rt1.userid = p.userid)
JOIN races r ON (r.raceid = rt1.raceid)
WHERE rt1.time = (SELECT MIN(rt2.time) FROM race_times rt2 WHERE rt1.raceid = rt2.raceid)
GROUP BY r.name;
or..
SELECT rt1.*, r.name, p.name
FROM race_times rt1
LEFT JOIN users p ON p.userid = rt1.userid
JOIN races r ON r.raceid = rt1.raceid
WHERE EXISTS (SELECT NULL FROM race_times rt2 WHERE rt2.raceid = rt1.raceid
GROUP BY rt2.raceid HAVING MIN(rt2.time) >= rt1.time);
I receive correct results as shown below:
raceid | name | userid | name | time |
-------+----------------------+--------+----------+--------|
1 | Doherty | 2 | Player 2 | 50000 |
3 | Easter Basin Naval S | 1 | Player 1 | 120516 |
6 | Fort Carson | 1 | Player 1 | 89092 |
4 | Glen Park | 1 | Player 1 | 101109 |
2 | Palomino Creek | 1 | Player 1 | 148039 |
7 | Tierra Robada | 1 | Player 1 | 77933 |
and here is the faulty query:
SELECT rt.raceid, r.name, rt.userid, p.name, rt.time
FROM race_times rt
LEFT JOIN users p ON p.userid = rt.userid
JOIN races r ON r.raceid = rt.raceid
GROUP BY r.name
HAVING rt.time = MIN(rt.time);
and the result is this:
raceid | name | userid | name | time |
-------+----------------------+--------+----------+--------|
3 | Easter Basin Naval S | 1 | Player 1 | 120516 |
6 | Fort Carson | 1 | Player 1 | 89092 |
4 | Glen Park | 1 | Player 1 | 101109 |
2 | Palomino Creek | 1 | Player 1 | 148039 |
7 | Tierra Robada | 1 | Player 1 | 77933 |
As you can see, race "Doherty" (raceid: 1) is owned by "Player 2" (userid: 2) and it is not shown along with the rest of race records (which are all owned by userid 1). What is the problem?
Regards,
Having is a post filter. The query gets all the results, and then further filters them based on having. The GROUP BY compacting the rows based on the group, which gives you the first entry in each set. Since player 1 is the first entry for race 1, that's the result that is being processed by the HAVING. It is then filtered out because its time does not equal the MIN(time) for the group result.
This is why the other ones you posted are using a sub-query. My personal preference is for the first example, as to me it's slightly easier to read. Performance wise they should be the same.
While it's not a bad idea to try and avoid sub queries in the where clause, this is mostly valid when you can accomplish the same result with a JOIN. Other times it's not possible to get the result with a JOIN and a sub query is required.

select from tables with different numbers of rows

I'm hoping there is a simple answer to this. Competitors race over a series of 3 races. Some competitors only show up for one race. How could I show a final result for ALL competitors?
race 1
+------+--------+
| name | result |
+------+--------+
| Ali | 30 |
| Bob | 28 |
| Cal | 26 |
+------+--------+
race 2
+------+--------+
| name | result |
+------+--------+
| Ali | 32 |
| Bob | 31 |
| Dan | 24 |
+------+--------+
race 3
+------+--------+
| name | result |
+------+--------+
| Eva | 23 |
| Dan | 25 |
+------+--------+
The final result should look like this:
+------+--------+--------+--------+
| name | result | result | result |
+------+--------+--------+--------+
| Ali | 30 | 32 | |
| Bob | 28 | 31 | |
| Cal | 26 | | |
| Dan | | 24 | 25 |
| Eva | | | 23 |
+------+--------+--------+--------+
The problem I have is with ordering by name from multiple tables.
Here is the example data:
CREATE TABLE race (name varchar(20), result int);
CREATE TABLE race1 LIKE race;
INSERT INTO race1 VALUES ('Ali', '30'), ('Bob', '28'), ('Cal', '26');
CREATE TABLE race2 like race;
insert INTO race2 VALUES ('Ali', '32'), ('Bob', '31'), ('Dan', '24');
CREATE TABLE race3 LIKE race;
INSERT INTO race3 VALUES ('Eva', '23'), ('Dan', '25');
Many thanks!
Here we go !!!
select race1.name as name, race1.result, race2.result, race3.result from race1
left join race2 on race2.name = race1.name
left join race3 on race3.name = race1.name
union
select race2.name as name, race1.result, race2.result, race3.result from race2
left join race1 on race1.name = race2.name
left join race3 on race3.name = race2.name
union
select race3.name as name, race1.result, race2.result, race3.result from race3
left join race1 on race1.name = race3.name
left join race2 on race2.name = race3.name;
It is working :)
select s.name,
max(case when s.R = 'Result1' then s.result else '' end) as result1,
max(case when s.R = 'Result2' then s.result else '' end) as result2,
max(case when s.R = 'Result3' then s.result else '' end) as result3
from
(
select 'Result1' as R,r1.* from race1 r1
union all
select 'Result2' as R,r2.* from race2 r2
union all
select 'Result3' as R,r3.* from race3 r3
) s
group by s.name
result
+------+---------+---------+---------+
| name | result1 | result2 | result3 |
+------+---------+---------+---------+
| Ali | 30 | 32 | |
| Bob | 28 | 31 | |
| Cal | 26 | | |
| Dan | | 24 | 25 |
| Eva | | | 23 |
+------+---------+---------+---------+
5 rows in set (0.00 sec)
I personally would create the schema in a different way.
One table for the users, one for the races and one that connects both:
-- Create syntax for TABLE 'races'
CREATE TABLE `races` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'users'
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'race_results'
CREATE TABLE `race_results` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`race_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`result` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Let's insert some data (should be equal to your data set).
-- Insert data
INSERT INTO users (name)values('Ali'),('Bob'),('Cal'),('Dan'), ('Eva');
INSERT INTO races (name)values('Race1'),('Race2'),('Race3');
INSERT INTO race_results (user_id, race_id, result)values(1,1,30),(2,1,30),(1,2,28),(2,2,31),(3,1,26),(4,2,24),(4,3,25),(5,3,23);
Then you could write the query like this:
-- Static version
SELECT us.name, sum(if(ra.name='Race1', result, null)) as Race1, sum(if(ra.name='Race2', result, null)) as Race2, sum(if(ra.name='Race3', result, null)) as Race3
FROM race_results as rr
LEFT JOIN users as us on us.id = rr.user_id
LEFT JOIN races as ra on ra.id = rr.race_id
GROUP BY us.id;
Which gives you the result you're looking for. (I changed the column names to make it more obvious which result belongs to which race.)
But I've to admit that this works fine for 3 races but what if you have 30 or more?
Here is a more dynamic version of the above query, which kind of creates itself ;)
-- Dynamic version
SET #sql = '';
SELECT
#sql := CONCAT(#sql,if(#sql='','',', '),temp.output)
FROM
(SELECT
CONCAT("sum(if(ra.name='", race.name, "', result, null)) as ", race.name) as output
FROM races as race
) as temp;
SET #sql = CONCAT("SELECT us.name,", #sql, " FROM race_results as rr LEFT JOIN users as us on us.id = rr.user_id LEFT JOIN races as ra on ra.id = rr.race_id GROUP BY 1;");
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Rewriting MySQL query without using GROUP BY

Here is the table information:
Table name is Teaches,
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| ID | varchar(5) | NO | PRI | NULL | |
| course_id | varchar(8) | NO | PRI | NULL | |
| sec_id | varchar(8) | NO | PRI | NULL | |
| semester | varchar(6) | NO | PRI | NULL | |
| year | decimal(4,0) | NO | PRI | NULL | |
+-----------+--------------+------+-----+---------+-------+
The requirement is to find which course appeared more than once in 2009(ID is the id of teachers)
Here is my query using GROUP BY:
select course_id
from teaches
where year= 2009
group by course_id
having count(id) >= 2;
How could I write this without using GROUP BY?
You may try this:
SELECT DISTINCT
T.course_id
FROM
teaches T
WHERE
T.course_id NOT IN (
SELECT
T1.course_id
FROM teaches AS T1 INNER JOIN teaches AS T2 ON T1.course_id = T2.course_id
AND T1.`year` = T2.`year`
AND T1.id <> T2.id
WHERE T1.`year` = 2009
);
Test Schema And Data:
DROP TABLE IF EXISTS `teaches`;
CREATE TABLE `teaches` (
`ID` varchar(5) CHARACTER SET utf8 DEFAULT NULL,
`course_id` varchar(8) CHARACTER SET utf8 DEFAULT NULL,
`sec_id` varchar(8) CHARACTER SET utf8 DEFAULT NULL,
`semester` varchar(6) CHARACTER SET utf8 DEFAULT NULL,
`year` decimal(4,0) DEFAULT NULL
);
INSERT INTO `teaches` VALUES ('66', '100', 'B', '11', '2009');
INSERT INTO `teaches` VALUES ('71', '100', 'A', '11', '2009');
INSERT INTO `teaches` VALUES ('64', '102', 'C', '12', '2010');
INSERT INTO `teaches` VALUES ('77', '102', 'B', '22', '2009');
Expected Output:
course_id
102
SQL FIDDLE DEMO
You could use something like this
select t.course_id
from (select t.*, count(*) as cnt from teaches t where year= 2009 ) t
where cnt > 1
Try to use stack overflow to search before posting a question,
Select Rows that appear more than once
Something like below should work:
select distinct course_id
from teaches o
where (select count(1) from teaches i where i.course_id = o.course_id and i.[year] = 2009) > 1
For your homework, below sql can be done.
This is followed your logic, id exist more than once is means course appeared more than once.
select DISTINCT T1.course_id
from teaches T1
where T1.course_id not in (
select a.course_id
from teaches as a inner join teaches as b
on a.course_id = b.course_id and a.year = b.year and a.id <> b.id
where a.year= 2009 )