Order a Table in MySql without sendtime - mysql

How can I query to find the most recent message in a table, when the values in the column sendTime are all null?
I have tried.
SELECT `from`
,MAX(column) AS most_recent_message
FROM table
GROUP BY `from`

if you have an auto increment primary key, then you can find the maximum primary key belongs to which column.
SELECT * FROM your_table WHERE id = (SELECT MAX(id) FROM your_table);

Related

MySQL - INSERT INTO … ON DUPLICATE KEY UPDATE with values select from the same table

I have two tables, one is posts(ID, edited_date, is_public, ... etc), another one is post_pagination(index, post_id) for storing the pagination data of posts. In post_pagination, index is primary key.
One step in the pagination process, I need to check the sorted result and post_pagination table, and do INSERT/UPDATE, like:
INSERT INTO post_pagination(`index`, post_id)
SELECT * /* full outer join new sorted result and pagination table */
FROM (
WITH
cte1 AS (
SELECT ROW_NUMBER() OVER(ORDER BY edited_date DESC, ID DESC) AS new_rank, ID
FROM posts
WHERE is_public = 1
),
cte2 AS (
SELECT `index`
FROM post_pagination
)
SELECT `index` AS dummy_rank, ID
FROM cte2
LEFT JOIN cte1 on `index` = new_rank
UNION ALL
SELECT new_rank AS dummy_rank, ID
FROM cte2
RIGHT JOIN cte1 on `index` = new_rank
WHERE `index` IS NULL
) AS a
ORDER BY dummy_rank
ON DUPLICATE KEY UPDATE post_id = ID
The first time run this query, post_pagination is empty, so MySQL insert all data into the table.
index post_id
1 390
2 391
3 392
4 393
5 307
it works well.
When I run the second time, I expect all data will not be changed, but it update all post_id field to the last value of the result.
index post_id
1 307
2 307
3 307
4 307
5 307
I did a few tests, it seems like inserting values into a table by using values select from the same table would cause this problem, but I can't figure out why
Now I simply fix this problem by modifying the last line of the query to:
ON DUPLICATE KEY UPDATE post_id = ID + 0
Is there any better way to solve this issue?
Can you instead use a temporary table?
CREATE TEMPORARY TABLE `post_pagination_temp` (
`index` INTEGER(1) AUTO_INCREMENT PRIMARY KEY,
`post_id` INTEGER(1)
);
INSERT INTO `post_pagination_temp` (`post_id`)
SELECT `ID` FROM `posts` ORDER BY `edited_date` DESC, `ID` DESC;
INSERT INTO `post_pagination`
SELECT * FROM `post_pagination_temp`
ON DUPLICATE KEY UPDATE `index` = VALUES(`index`);
This assumes you have the correct index on post_pagination, i.e. the only unique being the primary key on post_id.

Get random single row from mysql table with enumerated rows

I've got a table with auto-incremented ID in Mysql. I am always adding to this table, never deleting and setting the ID value to NULL so that I am pretty sure there are no holes. This is the table structure:
CREATE TABLE mytable (
id smallint(5) unsigned NOT NULL AUTO_INCREMENT,
data1 varchar(200) DEFAULT NULL,
data2 varchar(30) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY data (data1,data2)
)
I want to pick up a random row from the table. I am using this:
select * from mytable where id=(select floor(1 + rand() * ((select max(id) from mytable) - 1)));
But sometimes I get nothing, sometimes one row, sometimes two. Replacing max(id) with count(*) or count(id) did not help. I understand it may be because rand() is evaluated for each row. As suggested in a similar question, I used this query:
select * from mytable cross join (select #rand := rand()) const where id=floor(1 + #rand*((select count(*) from mytable)-1));
But I still get an empty set sometimes. Same goes for this:
select * from mytable cross join (select #rand := rand()) const where id=floor(#rand*(select count(*) from mytable)+1);
I am looking for a fast way to do this, so that it won't take a long on big tables. ORDER BY rand() LIMIT 1 is not an option for me. Can't that be done with one query, can be?

Select count of distinct column in table

Lets say I have a sql table in the format:
id INT PRIMARY KEY AUTO_INCREMENT,
action TINYINT NOT NULL,
user_id INT NOT NULL
I would like to build a query that produces the same result as the following:
SELECT
(SELECT COUNT(*), action FROM table WHERE action=1 AND user_id=1),
(SELECT COUNT(*), action FROM table WHERE action=2 AND user_id=1),
(SELECT COUNT(*), action FROM table WHERE action=3 AND user_id=1),
...
(SELECT COUNT(*), action FROM table WHERE action=n AND user_id=1)
(1<->n is simply the range of action)
But without all the redundancy and inefficiency.
If I understand the question correctly, this is what you're looking for:
SELECT action, COUNT(*)
FROM your_table
WHERE user_id = 1
GROUP BY action
You can use GROUP BY:
SELECT
COUNT(*) as Count,
action as Action
FROM table1
WHERE user_id = 1
GROUP BY action
This will produce a row for each unique action, and in that row have a column Count being the count of rows with that unique action.
In the example above, all user_id conditions are =1, so if this is case you can include it as in the query, otherwise you can form your own condition based on the needed values.
SELECT COUNT(id), action FROM table WHERE user_id=1 GROUP BY action
HTH, though I am a bit confused if this is what you are looking for.

Counting TINYINT values in MySQL

The table structure is like this:
actions: int(10)
unlock: tinyint(1)
user_id: int(20)
name: varchar(50)
I have such query:
SELECT SUM(actions) AS "sum_actions", SUM(unlock) AS "sum_unlock", user_id, name
FROM mytable AS `Results` WHERE user_id != 0 GROUP BY user_id
ORDER BY sum_actions DESC LIMIT 0,300
This gives #1064 - You have an error in your SQL syntax error.
When I remove SUM(unlock) AS "sum_unlock" then query works. So I thought that it is not possible to summing TINYINTs. So I changed to COUNT(unlock) as "count_unlock" but this didn't help. I don't want to change "unlock" table to INT because it only has boolean values. How can I count the unlock table with summing for each user_id ?
unlock is a reserved word. Try this:
SELECT SUM(actions) AS "sum_actions", SUM(`unlock`) AS "sum_unlock", user_id, name
FROM mytable AS `Results`
WHERE user_id != 0
GROUP BY user_id
ORDER BY sum_actions DESC
LIMIT 0,300
Here is a list of reserved words.
You can try SUM(CAST(unlock AS INT)) to count as if the column was an INT column without actually changing it to be an INT column:
SELECT
SUM(actions) AS "sum_actions",
SUM(CAST(unlock AS INT)) AS "sum_unlock",
user_id,
name
FROM
mytable AS `Results`
WHERE
user_id != 0
GROUP BY
user_id,
name
ORDER BY
sum_actions DESC
LIMIT 0,300

PHP SQL - Advanced delete query

I have a table with 3 columns: id, date and name. What I am looking for is to delete the records that have a duplicate name. The rule should be to keep the record that has the oldest date. For instance in the example below, there is 3 records with the name Paul. So I would like to keep the one that has the oldest date (id=1) and remove all the others (id = 4 and 6). I know how to make insert, update, etc queries, but here I do not see how to make the trick work.
id, date, name
1, 2012-03-10, Paul
2, 2012-03-10, James
4, 2012-03-12, Paul
5, 2012-03-11, Ricardo
6, 2012-03-13, Paul
mysql_query(?);
The best suggestion I can give you is create a unique index on name and avoid all the trouble.
Follow the steps as Peter Kiss said from 2 to 3. Then do this
ALTER Table tablename ADD UNIQUE INDEX name (name)
Then Follow 4 Insert everything from the temporary table to the original.
All the new duplicate rows, will be omitted
Select all the records what you want to keep
Insert them to a temporary table
Delete everything from the original table
Insert everything from the temporary table to the original
Like Matt, but without the join:
DELETE FROM `table` WHERE `id` NOT IN (
SELECT `id` FROM (
SELECT `id` FROM `table` GROUP BY `name` ORDER BY `date`
) as A
)
Without the first SELECT you will get "You can't specify target table 'table' for update in FROM clause"
Something like this would work:
DELETE FROM tablename WHERE id NOT IN (
SELECT tablename.id FROM (
SELECT MIN(date) as dateCol, name FROM tablename GROUP BY name /*select the minimum date and name, for each name*/
) as MyInnerQuery
INNER JOIN tablename on MyInnerQuery.dateCol = tablename.date
and MyInnerQuery.name = tablename.name /*select the id joined on the minimum date and the name*/
) /*Delete everything which isn't in the list of ids which are the minimum date fore each name*/
DELETE t
FROM tableX AS t
LEFT JOIN
( SELECT name
, MIN(date) AS first_date
FROM tableX
GROUP BY name
) AS grp
ON grp.name = t.name
AND grp.first_date = t.date
WHERE
grp.name IS NULL
DELETE FROM thetable tt
WHERE EXISTS (
SELECT *
FROM thetable tx
WHERE tx.thename = tt.thename
AND tx.thedate > tt. thedate
);
(note that "date" is a reserver word (type) in SQL, "and" name is a reserved word in some SQL implementations)