Table comment contains comment and its replies
CREATE TABLE `comment` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`comment` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
Table reply_to which has response_id and comment_id
both of which are the IDs of the records in the comment table.
CREATE TABLE `reply_to` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`response_id` int(11) NOT NULL,
`comment_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
)
If table comment has data like the following:
And table reply_to contains
And expected result like :
Question: How can I display all comments and their replies by using a left outer join?
This question came out from this page
Database design for comments and replies
How come the sql below not working here ?
SELECT c.comment, r.comment as reply
FROM Comment c
LEFT OUTER JOIN Comment r on c.id = r.id
LEFT OUTER JOIN reply_to rt on rt.response_id = r.id
It's still not totally clear what you want the results to look like, but the original query (the one with ANSI-89 joins) from the post you linked to gives the results I expected:
So I rewrote that query with proper joins, and it looks like this:
SELECT
oc.comment,
rc.comment
FROM
comment oc
INNER JOIN
reply_to r
ON r.comment_id = oc.id
INNER JOIN
comment rc
ON r.response_id = rc.id
If you want to see comments with no reply:
then you could use left outer joins like this:
SELECT
oc.comment,
rc.comment
FROM
comment oc
LEFT OUTER JOIN
reply_to r
ON r.comment_id = oc.id
LEFT OUTER JOIN
comment rc
ON r.response_id = rc.id
Related
I want to join multiple tables using JOINs, and mysql/mariadb is refusing to find one column. The column exists, of course, and I can't figure out what the cause might be.
Table Layout
CREATE TABLE `shops` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
CREATE TABLE `shops2categories` (
`shopid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL,
);
CREATE TABLE `categories` (
`id` int(11) NOT NULL,
`name` varchar(64) NOT NULL,
);
CREATE TABLE `categories2items` (
`itemid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL
);
CREATE TABLE `items` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
Query:
In order to avoid confusion with aliases, I now ran the query with the original table names.
SELECT
shops.name,
categories.name,
items.name
FROM shops
LEFT JOIN shops2categories ON shops2categories.shopid = shops.id
LEFT JOIN categories2items ON categories2items.categoryid = categories.id
LEFT JOIN categories ON categories.id = shops2categories.categoryid
LEFT JOIN items ON items.id = categories2items.itemid
Error Message:
#1054 - Unknown column 'categories.id' in 'on clause'
No matter how I restructured my query (foreign key first, primary key first, items table first, categories table first, ..., using different JOIN types), I can't seem to get this to work. Also I read through a whole lot of SO questions to this topic, but I feel that the order is correct, I am not inserting nor updating, and it's not about quoting. Something is fundamentally broken in my concept, and I'm very keen on learning what this could be.
Look at your from clause. You reference c.id before you have defined c.
A table cannot be referenced until it is defined in the FROM clause.
You would seem to want:
FROM shops s LEFT JOIN
shops2categories s2c
ON s2c.shopid = s.id LEFT JOIN
categories c
ON c.id = s2c.categoryid LEFT JOIN
categories2items c2i
ON c2i.categoryid = c.id LEFT JOIN
items i
ON i.id = c2i.itemid
I heard, that JOINS are more efficient, but when i try to implement it, then SELECT is stuck in executing.
I'm using MySQL and I have two tables filled with 100,000 records with a for loop.
CREATE TABLE `offers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY(`id`)
);
CREATE TABLE `files` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`path` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`object_id` int(11) NOT NULL,
PRIMARY KEY(`id`)
);
And following query takes 0,3s to execute:
SELECT o.id, (SELECT f.path FROM `files` AS f WHERE f.object_id = o.id LIMIT 1) as path FROM `offers` AS o;
But, this query which is working when files table is almost empty in exactly the same way. But when i fill files table, executing never finished.
SELECT o.id, f.path FROM `offers` o LEFT OUTER JOIN `files` f ON f.object_id = o.id
What i'm doing wrong?
for left join performance
SELECT o.id, f.path
FROM `offers` o
LEFT OUTER JOIN `files` f ON f.object_id = o.id
be suure you have a composite index
on table files column(object_id, path)
The Queries are working perfectly each one separatedly:
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
SELECT att.tournament_type_name, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att ON m2s.tournament_type_id = att.tournament_type_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
The tables 'atp_matchs_to_surfaces' and 'atp_matchs_to_tournament_type' are defined in that way:
CREATE TABLE IF NOT EXISTS `atp_matchs_to_tournament_type` (
`tournament_type_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`tournament_type_id`,`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_matchs_to_surfaces` (
`surfaces_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`surfaces_id`,`matchs_id`)
And the other Tables with all the data:
CREATE TABLE IF NOT EXISTS `atp_matchs` (
`matchs_id` int(7) NOT NULL AUTO_INCREMENT,
`tournament_name` varchar(36) NOT NULL,
`tournament_year` year NOT NULL,-- DEFAULT '0000',
`tournament_country` varchar(26) NOT NULL,
`match_datetime` datetime NOT NULL,-- DEFAULT '0000-00-00 00:00:00',
`match_link` varchar(85) NOT NULL,
`prize_money` int(12) NOT NULL,
`round` varchar(8) NOT NULL,-- DEFAULT '1R',
`sets` varchar(34) NOT NULL,-- DEFAULT '0-0',
`result` varchar(4) NOT NULL,-- DEFAULT '0-0',
`p1_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
`p2_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
PRIMARY KEY (`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_surfaces` (
`surfaces_id` int(4) NOT NULL AUTO_INCREMENT,
`surface_name` varchar(24) NOT NULL,
PRIMARY KEY (`surfaces_id`)
CREATE TABLE IF NOT EXISTS `atp_tournament_type` (
`tournament_type_id` int(4) NOT NULL AUTO_INCREMENT,
`tournament_type_name` varchar(22) NOT NULL,
PRIMARY KEY (`tournament_type_id`)
I want in the same Query all the records of match and surface name+tournament type. It's clear? I hope...
I tried to implement this with SubQueries: http://www.w3resource.com/mysql/subqueries/ and How can an SQL query return data from multiple tables but i can't do it to work.
OK, this is your current schema. As you can see, one match can be played on multiple surfaces and one match can be played within multiple tournament types.
If this schema is OK, you can get your result with this query:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_matchs_to_surfaces AS m2s ON m2s.matchs_id = am.matchs_id
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = m2s.surfaces_id
LEFT JOIN atp_matchs_to_tournament_type AS m2t ON m2t.matchs_id = am.matchs_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = m2t.tournament_type_id
However, if one match can be played on one surface only and within one tournament type only, I would change your schema to:
Tables atp_matchs_to_surfaces and atp_matchs_to_tournament_type are removed and fields surfaces_id and tournament_type_id moved to atp_matchs table. Your query is now:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = am.surfaces_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = am.tournament_type_id
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2).
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN (SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s) as......
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id
LEFT JOIN(
SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att AS Q1 ON m2s.surfaces_id = att.surfaces_id
LEFT JOIN atp_matchs am AS Q2 ON am.matchs_id = m2s.matchs_id);
I added some "AS" because I had the error: Every derived table must have its own alias. I'm a little lost here!
I am using MySQL to create a database of articles and categories. Each article has a category. I would like to make a feature for the admin panel that lists all the categories, but also includes the latest article for each category. The method I usually use is to fetch rows from the category table, loop through the results, and then create another query using something like FROM articlesWHERE category_id = {CATEGORY_ID} ORDER BY article_id DESC LIMIT 1. That method just seems like overkill to me and I am wondering if it can be done in one query(Maybe with joins and subqueries?).
This is the current query I have that fetches categories:
SELECT * FROM article_categories ORDER BY category_title ASC
These are my tables:
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(15) NOT NULL AUTO_INCREMENT,
`author_id` int(15) NOT NULL,
`category_id` int(15) NOT NULL,
`modification_id` int(15) NOT NULL,
`title` varchar(125) NOT NULL,
`content` text NOT NULL,
`type` tinyint(1) NOT NULL,
`date_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`attachment_id` int(15) NOT NULL,
`youtube_id` varchar(32) DEFAULT NULL,
`refs` text NOT NULL,
`platforms` varchar(6) NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `article_categories` (
`category_id` int(15) NOT NULL AUTO_INCREMENT,
`parent_id` int(15) NOT NULL,
`title` varchar(50) NOT NULL,
`description` text NOT NULL,
`attachment_id` text NOT NULL,
`enable_comments` tinyint(1) NOT NULL,
`enable_ratings` tinyint(1) NOT NULL,
`guest_reading` tinyint(1) NOT NULL,
`platform_assoc` tinyint(1) NOT NULL,
`allowed_types` varchar(6) NOT NULL,
PRIMARY KEY (`category_id`,`title`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
This is the query I have come up with so far:
SELECT
c.category_id, c.title, c.description,
a.article_id, a.category_id, a.title, COUNT(a.article_id) AS total_articles
FROM article_categories AS c
LEFT JOIN articles AS l ON (
SELECT
article_id AS article_id, category_id, title AS article_title
FROM articles AS l
WHERE l.category_id = c.category_id
ORDER BY l.article_id
DESC LIMIT 1)
LEFT JOIN articles AS a ON (c.category_id = a.category_id)
GROUP BY c.category_id
ORDER BY c.title ASC
The above query gives me the following SQL error:
Operand should contain 1 column(s)
Why is this happening?
You can return list of all the categories and recent article in each category using one query, Try this
SELECT C.*, A.*
FROM article_categories C
LEFT OUTER JOIN articles A ON c.category_id = A.category_id
WHERE
(
A.category_id IS NULL OR
A.article_id = (SELECT MAX(X.article_id)
FROM articles X WHERE X.category_id = C.category_id)
)
This will restrict the articles to just the highest article_id per category and make use of the indexes on those tables:
select
ac.category_id, ac.title, newa.article_id, newa.title article_title
from article_categories ac
left join articles newa on ac.category_id = newa.category_id
left join articles olda on newa.category_id = olda.category_id
and olda.article_id > newa.article_id
where olda.article_id is null
;
See this Demonstrated at SQLFiddle
Shoelace, I was browsing your other questions and saw that this was unresolved so I've decided to take a crack at it.
This is a little tricky, but I don't think it's too bad, assuming I understand your question correctly. First, get the latest article date for each category:
SELECT a.category_id, MAX(a.date_posted)
FROM articles a
JOIN article_categories c ON c.category_id = a.category_id
GROUP BY a.category_id;
Then, join that with your articles table on the condition that the category_id and date are equal and you have what you need:
SELECT ar.*
FROM articles ar
JOIN(SELECT a.category_id, MAX(a.date_posted) AS latestDateForCategory
FROM articles a
JOIN article_categories c ON c.category_id = a.category_id
GROUP BY a.category_id) t
ON t.category_id = ar.category_id AND t.latestDateForCategory = ar.date_posted;
SQL Fiddle.
I've got the SQL query below:
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
It returns all the rows (about 4000) in the messages table with additional columns coming from the numbers table. So far, this is what I would expect.
Now I left join this sub-query to another table, again using a left join:
SELECT message, sent_date
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
However, it only returns about 200 rows so many are missing. Since this is a left join I would expect all the rows from table1 to be in the result. Can anybody see what the issue is?
Edit:
So for information here are the 3 relevant tables (with irrelevant columns removed):
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` text CHARACTER SET utf8 NOT NULL,
`from_id` int(11) DEFAULT NULL,
`sent_date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `from_id` (`from_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=101553 ;
CREATE TABLE IF NOT EXISTS `numbers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`number` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6408 ;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2395 ;
You can try alternative method to debug the issue:
CREATE TEMPORARY table tmp1 AS SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers
ON messages. from_id = numbers.id;
and then see whether this query works.
SELECT message, sent_date
FROM tmp1 table1
LEFT JOIN users
ON table1.user_id = users.id;
Also for your case make sure that there are no other insert or updates in between. otherwise use transactions.
table1 sometimes won't have a UserID - so that'll be null, so those results will be missing?
I don't have an exact answer to your question, but if I have to start thinking, I will first find out what 3800 rows are missing and try to see the pattern (is it because user_id are null or duplicate)
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
MINUS
(SELECT table1.message, table1.sent_date, table1.user_id
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id)
Try this, I think it's a scoping issue on user_id.
SELECT table1.message, table1.sent_date
FROM (
SELECT messages.message, messages.sent_date, numbers.user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
I'm not sure if user_id is in messages or numbers.
There is no way this should happen.
Try this variation:
SELECT
m.message, m.sent_date, n.user_id
FROM
messages m
LEFT JOIN
numbers AS n ON m.from_id = n.id
LEFT JOIN
users AS u ON n.user_id = u.id ;