I have a messages table and, I would like to know what would be the most efficient query to accomplish the following result:
Note thread field is null if the thread is the very first message all other messages are linked to that thread with is the emid
CREATE TABLE `messages` (
`emid` BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`emitter` BIGINT(20) NOT NULL,
`receiver` BIGINT(20) NOT NULL,
`thread` BIGINT(20) DEFAULT NULL,
`opened` TINYINT(4) DEFAULT 0,
`message` BLOB NOT NULL,
`timecard` DATETIME DEFAULT CURRENT_TIMESTAMP,
ADD CONSTRAINT `efk` FOREIGN KEY (`emitter`) REFERENCES `members` (`user_id`),
ADD CONSTRAINT `rfk` FOREIGN KEY (`receiver`) REFERENCES `members` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I would like to get the first row for a given receiver including its messages count. Say for instance a user has 4 non opened messages (unread) and two replies. I would like to get the first message received under this thread and the total replies from both sides. At first I thought about sub queries but that seems like it will perform badly.
The following selects threads for user (receiver = id)
SELECT * FROM `messages` WHERE thread IS NULL AND receiver = 2 ORDER BY timecard DESC
This one get the message count under a given thread
SELECT COUNT(*) FROM `messages` WHERE thread = 20
Join your two queries:
SELECT m1.*, IFNULL(COUNT(m2.emid), 0) AS replies
FROM messages AS m1
LEFT JOIN messages AS m2 ON m2.thread = m1.emid
WHERE m1.thread is NULL
GROUP BY m1.emid
The WHERE clause selects just the starting message from each thread from table m1.
LEFT JOIN then relates that to all the replies to that thread, using the thread column to link them back to the original message. I've used LEFT JOIN so that messages with no replies will be included.
COUNT(m2.emid) then counts the number of related rows, which is the number of replies in the thread. COUNT() only counts non-null values; if there are no replies, LEFT JOIN sets this column to NULL, so you'll get a count of 0.
Related
I have 2 tables.
CREATE TABLE $media_table (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`options` longtext DEFAULT NULL,
`order_id` int(11) unsigned DEFAULT NULL,
`player_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`))
CREATE TABLE $category_table (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category` varchar(300) DEFAULT NULL,
`media_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`))
I get id, options, category for rows matching category 'foo','bar'. I also use limit to get only x number of results.
SELECT mt.id, mt.options, ct.category
FROM $media_table as mt
LEFT JOIN $category_table as ct
ON mt.id = ct.media_id
WHERE mt.player_id = %d AND ct.category IN ('foo','bar')
GROUP BY ct.media_id
ORDER BY mt.order_id
LIMIT $limit
This works as intended. But I dont know how to get total number of results?
I tried this but the count is not correct.
SELECT COUNT(mt.id), ct.category
FROM $media_table as mt
LEFT JOIN $category_table as ct
ON mt.id = ct.media_id
WHERE mt.player_id = %d AND ct.category IN ('foo','bar')
GROUP BY ct.media_id
Where I select all results without the limit (in my previous query) the count is correct.
If I had only one table with primary key id I would do this to get count:
SELECT COUNT(id) FROM table
I dont know how to apply the same to my query.
Edit: I found my answer here select count(*) from select
Question 1: Are you looking at the raw results of the query using a tool like phpMyAdmin or MySQL WorkBency or what?
Question 2: Will the ultimate query results be delivered to the client via a web browser or what?
Answer 1: "The SUM() function returns the total sum of a numeric column."
SELECT SUM(column_name) FROM table_name WHERE condition;
Answer Possibility 2: If the results will be delivered in a web browser you should be able to use PHP or some other server side language like MS Active Server Pages to add up he "COUNT" field of each result.
Answer Possibility 3: Alternative 1: Export the results to a CVS file and import into a spreadsheet.
Maybe some of these suggestions will get the wheels turning and help you find the solution you are looking for.
I have two tables with the following structure and example content. Table one has the membership_no set to the correct values, but table two has some incorrect values in the membership_no column. I am needing to query both tables and check to see when the membership_no values are not equal, then update table two's membership_no column with the value from table one.
Table One:
id membership_no
====================
800960 800960
800965 800965
Table Two:
id membership_no
====================
800960 800970
800965 800975
Update query so far. It is not catching all of the incorrect values from table two.
UPDATE
tabletwo
INNER JOIN
tableone ON tabletwo.id = tableone.id
SET
tabletwo.membership_no = tableone.membership_no;
EDIT: Including SHOW CREATE and SELECT queries for unmatched membership_no column values.
Table One SHOW:
CREATE TABLE `n2z7m3_kiduka_accounts_j15` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`membership_no` int(11) NOT NULL,
...
`membershipyear` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=800987 DEFAULT CHARSET=utf8
Table Two SHOW:
CREATE TABLE `n2z7m3_kiduka_accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`membership_no` int(11) NOT NULL,
...
`membershipyear` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=801072 DEFAULT CHARSET=utf8
SELECT query for unmatched membership_no column values:
SELECT
u.name,
a.membership_no as 'Joomla 1.5 accounts table',
j.membership_no as 'Joomla 3.0 accounts table'
FROM
n2z7m3_kiduka_accounts_j15 AS a
INNER JOIN n2z7m3_users AS u ON a.user_id = u.id
INNER JOIN n2z7m3_kiduka_accounts AS j ON a.user_id = j.membership_no
and a.membership_no != j.membership_no
ORDER BY u.name;
While Tim's Answer is perfectly valid, another variation is to add the filter qualifier to the ON clause such that:
UPDATE tabletwo
INNER JOIN
tableone ON tabletwo.id = tableone.id AND tabletwo.membership_no <> tableone.membership_no
SET
tabletwo.membership_no = tableone.membership_no;
This means that you don't have the WHERE filter so it will process all rows, but will act on only those with differing membership_no values. Because it is an INNER JOIN the results will be both tables or no tables (Skipped/NULL result).
EDIT:
If you suspect you have a problem still, what does the MySQL command respond, do you have a specific error notice? With 80k columns, it may take a while for the comand to actually process , so are you giving the command time to complete or is PHP or the system causing the command to abort due to execution time expiry? (Update your execution time on PHP and MySQL and rerun query just to see if that causes it to complete successfully?)
Suggestion
As another sggestion I think your UNIQUE KEY should also be your AI key so for both tables:
DROP INDEX `user_id` ON <table> #removes the current unique index.
then
CREATE UNIQUE INDEX `id` ON <table> #addes unique index to the A_I column.
You just need to add a WHERE clause:
UPDATE
tabletwo
INNER JOIN
tableone
ON tabletwo.id = tableone.id
SET
tabletwo.membership_no = tableone.membership_no
WHERE tabletwo.membership_no <> tableone.membership_no
I'm trying to select the one row per user ID for a conversation list. I'm trying to show users a list of their active conversations (similar functionality can be shown on Facebook's messages page, it shows all of your conversations with the profile name and the latest message, then you click on them to see the entire chat history), but only displaying the last message from each user they're chatting with. Here's the query I tried using so far to select a single message from each user they're chatting with, but it's showing the oldest message. I tried changing the ORDER by it didn't help, so any help offered is appreciated:
SELECT `conversations`.`from_id`,
`profile`.`username`,
`conversations`.`date`,
`conversations`.`body`
FROM
`website`.`conversations`,
`website`.`profile`
WHERE
`conversations`.`to_id` = ?
AND `profile`.`profile_id` = `conversations`.`from_id`
GROUP BY `profile`.`user_id`
ORDER BY `conversations`.`id` DESC
LIMIT 15
I'm ordering by the conversation id (auto incrementing ID), so I was trying to get the highest ID (which would be the newest record) to be returned but I'm doing something wrong.
Update: Here's the table structure:
CREATE TABLE `conversations` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`to_id` int(10) unsigned NOT NULL,
`from_id` int(10) unsigned NOT NULL,
`body` mediumtext NOT NULL,
`date` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `to_id` (`to_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I've tried multiple queries from other "similar" questions (obviously editing them to make them reflect my table), none of which worked.
Update again: I tried the following query but now it is returning just one single row, and I'm hoping to return a maximum of 15 (LIMIT 15 in my previous query) unique conversations:
SELECT p1.id, p1.body, profile.username
FROM profile, conversations p1 LEFT JOIN conversations p2
ON (p1.to_id = p2.to_id AND p1.id < p2.id)
WHERE p2.id IS NULL AND p1.to_id = *INSERT_USER_ID* AND profile.user_id=p1.from;
I have the following (simplified) database schema:
CREATE TABLE IF NOT EXISTS `wm_renderings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`formula_id` int(11) NOT NULL,
`creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`svg` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `formula_id` (`formula_id`)
);
where formula_id is a foreign key.
I want to get the latest rendering for every formula_id. But when I write
SELECT `id`, `formula_id`, `svg`
FROM `wm_renderings`
GROUP BY `formula_id`
ORDER BY `creation_time` DESC
I would get a "random" rendering for each formula_id.
My approach would be to get all formula ids and then send a query for every single formula_id:
SELECT `id`, `formula_id`, `svg`
FROM `wm_renderings`
WHERE `formula_id` = 42
ORDER BY `creation_time` DESC
LIMIT 1
However, that would be a lot of queries.
Can I get the same with only one query?
The way to get the max/latest per group could be done is various way and one such way is to use left join
select t1.* from wm_renderings t1
left join wm_renderings t2 on t1.formula_id = t2.formula_id
and t1.creation_time < t2.creation_time
where t2.id is null
Here is the documentation on it
http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
Left join and Uncorrelated subs-query are considered to be better in terms of performance.
I have a table with multiple rows per "website_id"
CREATE TABLE `MyTable` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`tagCheckResult` int(11) DEFAULT NULL,
`website_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IX_website_id` (`website_id`),
) ENGINE=InnoDB;
I am trying to select the latest entry per website_id
-- This creates a temporary table with the last entry per website_id, and joins it
-- to get the entire row
SELECT *
FROM `WebsiteStatus` ws1
JOIN (
SELECT MAX(id) max_id, website_id FROM `WebsiteStatus`
GROUP BY website_id) ws2
ON ws1.id = ws2.max_id
Now, I know the correct way to get the last row per website_id is as above. My qusetion is - I also tried the following simpler query, at it seemed to return the exact same results as above:
SELECT * FROM `WebsiteStatus`
GROUP BY website_id
ORDER BY website_id DESC
I know that in principle GROUP BY without operators (e.g. MAX), like I do in my 2nd query can return any of the relevant rows ... but in practice it returns the last one. Is there an implementation detail in mysql that guarantees this is always the case?
(Just asking for academic curiosity, I know the 1st query is "more correct").