MySQL Query Order by the query itself - php

I have this query:
SELECT `name` FROM `products` WHERE `id` = 0 OR `id` = 4 OR `id` = 2
basically, I want the query to be sorted by the order of the OR statements (basically, the first returned object would be with ID 0, the second would be 4, and third be 2).
Is this possible?

Yes you need to use field() function in order by as
SELECT `name` FROM `products` WHERE `id` = 0 OR `id` = 4 OR `id` = 2
order by field(id,0,4,2)
Here is a demo
The table is not the same in your case but you may get an idea how it works.

Try this
select name from products WHERE id=0 OR id= 4 OR id=2 order by FIELD(id,0,4,2);
FIELD() Returns the index (position) of 0 in the 4, 2,... list. Returns 0 if 0 is not found.
Note : this will Slow down your query a bit.

I believe you are looking for the FIND_IN_SET() function:
SELECT name FROM products WHERE id IN (0,4,3) ORDER BY FIND_IN_SET(id, '0,4,3');

This is another idea -
SELECT name FROM products
WHERE id = 0 OR id = 4 OR id = 2
ORDER BY CASE WHEN id=0 THEN 1 WHEN 'id'=4 THEN 2 WHEN 'id'=2 THEN 3 ELSE 'id' END

Related

Mysql limit results based on value from column

I'm trying to limit the number of results of a query based on another table column. For example, I have a table for products and a config table, like this:
tb_product
id | active | name | value | ...
tb_config
max_product | ...
What I'd like to do is something like this
SELECT
a.name, a.value
FROM
tb_product a,
tb_config b
WHERE a.active = 1
LIMIT b.max_product
But I'm getting errors like #1327 - Undeclared variable: b. Is there a way to achieve this result?
Because currently what I'm doing is doing another query to get just the max_product value and then use it as php variable to limit the results, like this:
$limit = "SELECT max_product FROM tb_config";
SELECT name, value FROM tb_product WHERE ativo = 1 LIMIT $limit
Maybe....
SELECT a.name
, a.value
FROM tb_product a
CROSS JOIN (SELECT #Limit:=(SELECT max_product from tb_config))
WHERE a.active = 1
LIMIT #Limit
With help from #ENargit's answer in Variable LIMIT Clause in MySQL, you can do it using a row count variable.
Assuming the following schema:
CREATE TABLE
tb_product
(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(10),
`value` VARCHAR(10)
);
INSERT INTO
`tb_product`
(`name`, `value`)
VALUES
('Name1','Value1'),
('Name2','Value2'),
('Name3','Value3'),
('Name4','Value4'),
('Name5','Value5'),
('Name6','Value6'),
('Name7','Value7'),
('Name8','Value8'),
('Name9','Value9'),
('Name10','Value10');
CREATE TABLE
`tbl_config`
(
id INT PRIMARY KEY AUTO_INCREMENT,
`type` VARCHAR(10),
`value` INT
);
INSERT INTO
`tbl_config`
(`type`,`value`)
VALUES
('something',10),
('maxrows',7);
You can reference the config table with a subquery:
SELECT * FROM (
SELECT
tb_product.*,
#rownum := #rownum + 1 AS RowNum
FROM tb_product,
(SELECT #rownum := 0) AS CounterTbl
) AS DataTbl
WHERE
RowNum <= (
SELECT
`value`
FROM
`tbl_config`
WHERE
`type` = 'maxrows'
);
Gives you the first 7 rows (according to the config value). You can obviously extend this to do sorting etc.
SQLFiddle: http://www.sqlfiddle.com/#!9/f789e4/2

Displaying rolls from MySQLi skiping one roll

Im looking to display rolls from a MySQLi but I would like to display one yes and one no... example
If I use this code:
$sqldisplay = $mysqli->query("SELECT `id` FROM `albums` ORDER BY `id` DESC LIMIT 5");
while ($rowdisplay = $sqldisplay->fetch_assoc()) {
echo $rowdisplay['id'].'<br>';
}
It will display
10
9
8
7
6
But im looking to display it like this:
10
8
6
4
2
Is it posible? And if so, how can it be done?
Thanks for the help! :D
To display alternating rows, first double your limit.
$sqldisplay = $mysqli->query("SELECT `id` FROM `albums` ORDER BY `id` DESC LIMIT 10");
Then use a boolean switch to determine whether each row will be displayed.
$display = true;
while ($rowdisplay = $sqldisplay->fetch_assoc()) {
if ($display) echo $rowdisplay['id'].'<br>';
$display = !$display; // switch the display on/off
}
This way, you won't have to depend on the value of the ID, something that could produce unexpected results if any IDs are missing due to deleted rows, etc. A numeric ID is a surrogate key which should have no meaning other than uniquely identifying a row.
Using WHERE (id % 2) = 0 would give you only even numbers, and using WHERE (id % 2) > 0 would get you odd numbers.
You can use this along with a subquery that selects MAX(id) and returns either 1 (for odd) or 0 (for even). This will ensure that if the id is even, just even IDs will be returned, and vice-versa, when applied with the logic explained in the paragraph above.
SELECT `id`
FROM `albums`
GROUP BY `id`
HAVING `id` % 2 = (CASE WHEN (SELECT MAX(`id`) FROM `albums`) % 2 = 0 THEN 0 ELSE 1 END)
ORDER BY `id` DESC
LIMIT 5
With Paul's comment below, it's been pointed out that you can clean up the query even more, just doing WHERE id % 2 = (SELECT MAX(id) % 2 FROM albums) instead - this way you shouldn't need any GROUP BY..HAVING.
SELECT `id`
FROM `albums`
WHERE id % 2 = (SELECT MAX(id) % 2 FROM albums)
ORDER BY `id` DESC
LIMIT 5
You can also achieve this in PHP if you wish retrieve both datasets, see Don't Panic's answer for that (although I prefer to do it in MySQL if possible).
MySQL modulus % documentation
when you set id for order by.your result is According to insert each column
you must add column in you table for this .for example you can have column orderBy and you can specify the order of each display column
and then your query same
$sqldisplay = $mysqli->query("SELECT `id` FROM `albums` ORDER BY `orderBy` DESC LIMIT 5");
Simple, just check if the modulo of the ID is 0 (no remainders) to return even numbers.
If you want to return odd numbers, change the modulo to equal 1.
If you want to select odd or even numbers depending on user input, you can easily make an if statement and use Prepared Statements to input the requested number (1 for odd ID rows to be returned, 0 for even rows).
SELECT `id` FROM `albums` WHERE `id` % 2 = 0 ORDER BY `id` DESC LIMIT 5
So your code will be:
$sqldisplay = $mysqli->query("SELECT `id` FROM `albums` WHERE `id` % 2 = 0 ORDER BY `id` DESC LIMIT 5");
while ($rowdisplay = $sqldisplay->fetch_assoc())
{
echo $rowdisplay['id'].'<br>';
}

Mysql update with an incremental value

I want make an update in one request (if possible) for make less mysql request..
I've a table with differents columns and a column "order" and I want change the value in the "order"
I can make a loop foreach in PHP, but each loop is a request like this:
$i = 1;
foreach($tables as $table){
$sql = 'UPDATE `table` SET `order` = '$i' WHERE `id` ='.$table['id'].';';
$i++;
}
Does it possible to make only one request ?
I've find this but it don't work
SET #pos := 0;
$sql ='UPDATE table SET order = ( SELECT #pos := #pos + 1 );';
Or I don't know how to make it work ..
(Posted on behalf of the OP).
I've understand that's not possible directly with MySQL query. So I'll make a loop for update each row, one by one.
This approach can solve your problem with a single query, but it will be tedious if we have more records
Let us have a table named order_details
CREATE TABLE `order_details` (
`order_id` int(11) NOT NULL,
`order_description` varchar(45) DEFAULT NULL,
`order_value` int(11) DEFAULT NULL,
PRIMARY KEY (`order_id`)
)
Select * from order_details
order_id order_description order_value
1 a 11
2 b 12
3 c 13
Now updating multiple rows in a single query
UPDATE test.order_details
SET order_value = CASE order_id
WHEN 1 THEN 21
WHEN 2 THEN 22
WHEN 3 THEN 23
END WHERE order_id IN (1,2,3)
You can verify it by executing a select query
Select * from order_details
order_id order_description order_value
1 a 21
2 b 22
3 c 23
Why not combine all the sql statements into 1 long string by keeping semi-colon (;) between each then send everything at once
foreach($tables as $table){
$sql .= 'UPDATE `table` SET `order` = '$i' WHERE `id` ='.$table['id'].';';
$i++;
}
mysqli_multi_query($sql); //do this after loop completes
Or did I misunderstand your request?

how to delete records in a table in mysql but keep first x records

I want to write a query in mysql for delete action. I want to keep first X records and for example only delete records after row number 40. I mean I want to keep first x rows and delete all rows after that.
how can I write this query?
I can delete first rows but I don't know how I can delete rows after row number X
I tried this code but this is not correct and not working.
DELETE FROM `myTable` WHERE `username` = 'name' ORDER BY `DD` DESC LIMIT 40 OFFSET 3
EDIT:
DELETE FROM `myTable` WHERE `id` in ( select `id` from `myTable` where `username` = 'name' ORDER BY `DD` DESC Limit 40, 18446744073709551615)
LIMIT 40 OFFSET 3 is for PostgreSQL.
For MySql you can try LIMIT 40,X
40 = starting at row 40
X = quantity of rows to delete after that
Obligatory Disclaimer: There are no "first rows" intrinsic to a relational database table.
However, since you've provided an ORDER BY, something like this should work (maybe):
SELECT `DD` INTO #dd40
FROM `myTable`
WHERE `username` = 'name'
ORDER BY `DD` ASC
LIMIT 39, 1
;
DELETE FROM `myTable`
WHERE `username` = 'name'
AND `DD` > #dd40
;
delete a
from sales.xxx_log_xxx as a
join (
select b.code,b.up_date
from sales.xxx_log_xxx as b
where b.code = 'code'
order by b.up_date desc limit 5,50) as aa
on a.code = aa.code
and a.up_date = aa.up_date

MYSQL working slowly as query with subquery than 2 queries (+php)

I have table (about 80'000 rows), looks like
id, parentId, col1, col2, col3...
1, null, 'A', 'B', 'C'
2, 1, ...
3, 1, ...
4, null, ...
5, 4, ...
(one level parent - child only)
and I need get all dependent rows -
SELECT ...
FROM table
WHERE id = :id OR parentId = :id OR id IN (
SELECT parentId
FROM table
WHERE id = :id
)
but why this request working slowly instead 2 request - if I get parentId on php first?
$t = executeQuery('SELECT parentId FROM table WHERE id = :Id;', $id);
if ($t) {
$id = $t;
}
$t = executeQuery('SELECT * FROM table WHERE id = :id OR parentId = :id ORDER BY id;', $id);
PS: max depends rows < 70
PPS:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY product ALL PRIMARY,parentId NULL NULL NULL 73415 Using where
2 DEPENDENT SUBQUERY product const PRIMARY,parentId PRIMARY 4 const 1
Change the IN for an equal =
SELECT ...
FROM table
WHERE id = :id OR parentId = :id OR id = (
SELECT parentId
FROM table
WHERE id = :id
)
or change it to a join:
SELECT ...
FROM table
inner join (
SELECT parentId
FROM table
WHERE id = :id
) s on s.parentID = table.id or s.parentID = table.parentID
Well, in the first case, MySQL need to create an intermediate result, store it in memory and then iterate over it to find all the relevant id in the table. In the second way, assuming you correctly created an index on id and parent id, it simply go straigth to the index, find the relevant rows, and send you back the result immediately.
UNION works faster for this case
this allows first query to user UNION INDEX and second just uses inner join, then merges results.
SELECT *
FROM `table`
WHERE id = :id OR parentId = :id
UNION
SELECT t1.*
FROM `table` t1 JOIN `table` t2 ON t2.parentId = t1.id AND t2.id = :id
An EXPLAIN might shed some more light on the problem for you.
Look into EXISTS, or rewriting your query as a JOIN.
It's a long shot but in first case you have "IN" statement of the WHERE part of the query. Maybe MySQL tries to optimize the query as if there would be multiple options and in the second case there is no IN part, so the compiled query is more straight forward for the database - thus utilizing the indexes in better manner.
Basically for 2 queries on the same connection the overhead of performing the queries should be minimal and irelevant in this case. Also subqueries in general are not very optimizable by the query parser. Try using JOIN instead (if possible).

Categories