I have a table and it's structured the following way:
CREATE TABLE `table` (
`type` int(11) NOT NULL,
`value` int(11) NOT NULL,
+ Some additional fields
KEY `type` (`type`)
)
What's the most efficient way to write the following query: Select the item with the maximal value of some certain type, let's say 'type0'.
If it's possible, could you also explain, what happens beneath the query (I mean, something that can affect the resulting algorithmic complexity).
I think it's
SQL Server:
SELECT TOP 1 *
FROM 'table'
WHERE type = 'type0'
ORDER BY 'value' DESCENDING
MySQL:
SELECT *
FROM 'table'
WHERE type = 'type0'
ORDER BY 'value' DESC
LIMIT 1
I guess. The most important part is that you have index on both 'type' and 'value'
Thanks #Andrew for pointing he is asking for MySQL. Cheers.
Finding just one row (with maximal value):
SELECT *
FROM tableX
WHERE type = 'type0'
ORDER BY value DESC
LIMIT 1
Finding all rows with same (maximal) value:
SELECT *
FROM tableX
WHERE type = 'type0'
AND value =
( SELECT MAX(value)
FROM tableX
WHERE type = 'type0'
)
And index on (type, value) (for InnoDB table) or on (type, value, PK) (for MyISAM table) will be useful.
Related
I keep searching but I can only find answers on the existence in a table but that is not what I am looking for.
I want to know how I would check the existence of a specific keyword inside the entire database.
I tried selecting from every table but got an error:
SELECT * FROM dvd_drives, compatible WHERE mpn = "700577-1C6"
#1052 - Column 'mpn' in where clause is ambiguous
I can use search inside phpmyadmin but how can I use this in a query?
SELECT EXISTS ( SELECT NULL
FROM table1_name
WHERE column_name = 'value'
UNION ALL
SELECT NULL
FROM table2_name
WHERE column_name = 'value'
UNION ALL
... ) AS check_result;
SELECT EXISTS ( SELECT NULL
FROM table1_name
WHERE column_name = 'value' )
*
EXISTS ( SELECT NULL
FROM table2_name
WHERE column_name = 'value' )
*
... ) AS check_result;
The queries checks the existence only (as required in the question), rows with specified value are not returned.
1st query check does the value is present in at least one of the tables, 2nd checks that it is present in each table at least once.
PS. NULL may be replaced freely with any literal value - zero, one, some string literal, etc...
I have a table (scrom_scoes_track table in bitnami moodle) which includes userid and value columns. Structure of the table is as follows.
I want to find the max mark of each user. Mark of the user can be found using the following query.
SELECT
`userid`,
`value`
FROM
`mdl_scorm_scoes_track`
WHERE
`element`= 'cmi.core.score.raw'
The result of the above query is as follows.
But when I tried to get max value using the following query it won't work as expected.
SELECT
`userid`,
MAX(`value`)
FROM
`mdl_scorm_scoes_track`
WHERE
`element`= 'cmi.core.score.raw'
GROUP BY
`userid`
Result of above query.
Here first row is userid 2 and value 50. But max value for userid 2 should be 100. (As shown in second image). Any help on this problem is highly appreciated.
Do not use longtext for a numeric field.
alternatively use below
SELECT `userid`,MAX(cast(`value` as unsigned)) FROM `mdl_scorm_scoes_track` WHERE `element`= 'cmi.core.score.raw' GROUP BY `userid`
First you need to convert your string into a number. Otherwise max() is sorting alphabetically
SELECT userid, max(cast(value as unsigned))
FROM mdl_scorm_scoes_track
WHERE element = 'cmi.core.score.raw'
group by userid
You have the right idea, but value is stored as longtext, so when you apply max on it, you get the "largest" value by lexicographical ordering. Casting it to an integer should solve the problem:
SELECT `userid`,MAX(CAST(`value` AS UNSIGNED))
FROM `mdl_scorm_scoes_track`
WHERE `element`= 'cmi.core.score.raw'
GROUP BY `userid`
My guess is that the value column isn't a numeric field
Try casting the value field eg CAST(value AS UNSIGNED)
You can do that without a GROUP BY statement.
Just list all rows as usual, but only select the maximum value from a second copy of the table. Then link the 2 copies together.
SELECT
`userid`,
`value`
FROM
`mdl_scorm_scoes_track` as outer
WHERE
`element`= 'cmi.core.score.raw'
and `value` = (
select
max(`value`)
from
`mdl_scorm_scoes_track` as inner
where
inner.userid = outer.userid )
The correct solution is to convert to a number. However, this is a case where I much prefer implicit conversion over explicit conversion:
SELECT userid, max(value + 0)
FROM mdl_scorm_scoes_track
WHERE element = 'cmi.core.score.raw'
GROUP BY userid;
This should work for any type of number -- negative numbers and numbers with decimal points, in particular.
i have a table like
create table `my_table`
(
`id` int auto_increment primary key,
`foreign_id` int not null,
`some_data` text,
`some_timestamp` timestamp default CURRENT_TIMESTAMP not null
);
I need to get unique rows both per year(got from timestap) and foreing_id , and get it from table ordered by timestamp
i've tried query :
SELECT *
FROM (
SELECT *
FROM `my_table`
ORDER BY `some_timestamp` DESC) as lr
GROUP BY YEAR(lr.`some_timestamp`),lr.`foreign_id`;
but this one not ordering by timestamp, idk why.
Other query worked ok untill i've loaded it to other server:
SELECT * FROM `my_table`
WHERE `id`,`some_timestamp` IN
(
SELECT `id`,max(`some_timestamp`)
FROM `my_table` lr2
GROUP BY YEAR(lr2.`some_timestamp`),`lr2`.foreign_id
);
on my pc this query executes around 3 seconds, on other server it get's error 504 gateway timeout.
Please help me to find best solution for this task.
You're making things too complicated. It didn't order correctly because GROUP BY does an implicit sorting, when no ORDER BY is specified.
Simply do it like this:
SELECT some_timestamp, foreign_id, MAX(some_timestamp) AS most_recent_timestamp_per_year
FROM `my_table`
GROUP BY YEAR(some_timestamp), foreign_id
ORDER BY `some_timestamp` DESC
I'm not sure, what your final query is supposed to do.
Also note, that you don't use group by correctly.
Every column in the select clause must either be specified in the group by clause or have an aggregate function applied to it. Otherwise you get random rows per group.
To order this query by date you need to rewrite it like this:
SELECT *
FROM (
SELECT *
FROM `my_table`) as lr
GROUP BY YEAR(lr.`some_timestamp`),lr.`foreign_id`
ORDER BY YEAR(lr.`some_timestamp`) DESC;
About execution time for second query.
If you don't want to dig in, try to add index on server DB to columns some_timestamp and foreign_id. It probably speed up the execution time.
Try something like this in mysql shell:
`CREATE INDEX my_request ON my_table (foreign_id, some_timestamp);
More about indexes you can find here
Recently i was trying to query this
SELECT * FROM `posts` WHERE `id` = '1a6' ORDER BY `date_created` DESC
but mysql is returning the row with ID = 1, it treated it as
SELECT * FROM `posts` WHERE `id` = '1' ORDER BY `date_created` DESC
i was trying to check for no items found but apparently mysql returned that row which is wrong.
the ID is primary and AI, so what's the solution to make that query STRICT only to what's withing the quotes, it's like mysql is ignoring what's after the numeric value in the query.
If ID is autoincrement ("AI") then it must be numeric. You should not quote its values as if they were strings. If 1a6 is supposed to be a hexadecimal literal, however, then you omitted the leading X:
WHERE `ID` = X'1a6'
Try with "LIKE" operator ...
SELECT * FROM `posts` WHERE `id` LIKE '1a6' ORDER BY `date_created` DESC
SELECT * FROM posts WHERE id LIKE '1%' ORDER BY date_created DESC
I have the following select statement to grab the next scheduled item for a stream. If there is no matching row, I want it to return a default value.
Here's the SQL that I'm using:
SELECT `file`
FROM `show`, `schedule`
WHERE `channel` = 1
AND `start_time` <= UNIX_TIMESTAMP()
AND `start_time` > UNIX_TIMESTAMP()-1800
AND `show`.`id` = `schedule`.`file`
ORDER BY `start_time`
DESC LIMIT 1
That should grab the most recently scheduled item, but not if it's older than 30 minutes before the query.
However, if the user doesn't schedule anything, I want a default value, so that something actually plays on the stream. I've tried the following:
SELECT COALESCE(`file`, 'default.webm')
FROM `show`, `schedule`...
And
SELECT IFNULL(`file`, 'default.webm')
FROM `show`, `schedule`
However, it always returns an empty result if no rows are found. How can I return a default value instead?
One way to do it
SELECT IFNULL(MIN(`file`), 'default.webm') `file`
FROM `show`, `schedule`
WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP()
AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file`
ORDER BY `start_time` DESC LIMIT 1
Since you return only one row, you can use an aggregate function, in that case MIN(), that ensures that you'll get NULL if no records selected. Then IFNULL() or COALESCE() will do its job.
#peterm's answer and this answer are designed to accommodate SQL logic that will return a maximum of one row in the result set.
His answer is designed to return a single row with a single column.
My answer is designed to return a single row with one or more columns.
Essentially, you can just use UNION with hardcoded value(s) in a second SELECT clause that has the same number of columns as the first SELECT.
For the OP:
(
SELECT `file`
FROM `show`,
`schedule`
WHERE `channel` = 1
AND `start_time` BETWEEN UNIX_TIMESTAMP()-1799 AND UNIX_TIMESTAMP()
AND `show`.`id` = `schedule`.`file`
ORDER BY `start_time` DESC
LIMIT 1
) UNION (
SELECT 'default.webm'
)
ORDER BY file = 'default.webm'
LIMIT 1;
Barring any syntax errors, the result set will serve up one row with one column keyed as file.
As a more general example: (DB-Fiddle Demo)
(
SELECT Col1,Col2,Col3
FROM ExampleTable
WHERE ID='1234'
) UNION (
SELECT 'Def Val','none',''
)
ORDER BY Col1 = 'Def Val'
LIMIT 1;
Outcomes:
If there are no rows found in the first SELECT, the result set will be filled with the values from the second SELECT. The result set as an array would be:
[['Col1' => 'Def Val', 'Col2' => 'none', 'Col3' => '']]
If one row is found in the first SELECT, the first SELECT values are offered in the result set and the second SELECT values are omitted. The result set as an would be: (see my demo link)
[['Col1' => 'A', 'Col2' => 'B', 'Col3' => 'C']]
*The associative keys in the result set will be dictated by the column names / aliases in the first SELECT query.
*Subsequent SELECT queries do not need to bother nominating column aliases.
*UNION doesn't require the column names from the two united queries to be identical. In fact, the column names or data sources in subsequent SELECT queries may be anything (different columns, function calls, etc).
(
SELECT Col1,Col2,Col3
FROM ExampleTable
WHERE ID='1234'
) UNION (
SELECT 'Def Val' AS `Fallback1`,
'none' AS `Fallback2`,
'' AS `Fallback3`
)
ORDER BY Col1 = 'Def Val'
LIMIT 1;
My opinion is that this is very easy to read and doesn't seem like a taxing query.
Thanks to #LudovicKuty for catching a potential bug regarding the order of rows produced by the UNION. https://dev.mysql.com/doc/refman/8.0/en/union.html#union-order-by-limit To eliminate the possibility of the default row being ordered before the found row, write an ORDER BY clause with enough logic to ensure the default row is always ordered later in the result set. There will be different sql-dialect-specific syntaxes that can be used to identify the default row. In some ORDER BY columnName = 'default value' will be enough, others may demand IF or IIF or CASE, etc. So long as the you build the logic so that the default returns a truthy result, then true will be treated as 1 and false will be treated as 0 -- and of course 1 comes after 0 when sorting ascending.
To handle a wider variety of cases, you'll need some conditional logic. This is only available in stored procedures in MySQL so you'll need to wrap this code in a procedure and call it:
if exists (
SELECT `file` FROM `show`, `schedule`
WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP()
AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file`
) then
SELECT `file` FROM `show`, `schedule`
WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP()
AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file`
ORDER BY `start_time` DESC LIMIT 1
; else
select `DefaultValue` as `file`
; end if