Mysql update with an incremental value - php

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?

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

how to batch update the unique column based on different condition in a single query mysql

======= products table ======
CREATE TABLE `products` (
`id` BigInt( 11 ) UNSIGNED AUTO_INCREMENT NOT NULL,
`item` VarChar( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`category` VarChar( 20 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY ( `id` ),
CONSTRAINT `unique_item` UNIQUE( `item` ) )
CHARACTER SET = utf8
COLLATE = utf8_general_ci
ENGINE = InnoDB
AUTO_INCREMENT = 1;
====== existing data in products table ======
INSERT INTO `products` ( `category`)
VALUES ( '2' ),( '2' ),( '3' ),( '5' );
====== update batch query ======
UPDATE products
SET item = CASE
WHEN category = 2
THEN 222
WHEN category = 2
THEN 211
WHEN category = 3
THEN 333
WHEN category = 5
THEN 555
END
WHERE category IN (2, 3, 5) AND item IS NULL
I have 3 columns (id, category, item) in products table.
id is primary key and item is unique key.
I want to use a single query to update item at once based on different category. The above update batch query is only working for category 3 and 5. It does not work for category 2.
The error message:
Error( 1062 ) 23000: "Duplicate entry '222' for key 'unique_item'"
I can use SELECT and UNION to get the list of id and put the id in CASE WHEN for update. But I would like to reduce one step. Is it possible to update all items in one query?
Thanks in advance!
===== EDITED =====
Current data in the product table
Expected result after run update query
It is possible to create a single query and update all data in one step.
Generate the folowing subquery in PHP from your update data:
select 0 as position, 0 as category, 0 as item from dual having item > 0
union all select 1, 2, 222
union all select 2, 2, 221
union all select 1, 3, 333
union all select 1, 5, 555
The first line here is only to define the column names and will be removed due to the impossible HAVING condition, so you can easily add the data rows in a loop without repeating the column names.
Note that you also need to define the position, which must be sequential for every category.
Now you can join it with your products table, but first you need to determine the position of a row within the category. Since your server probably doesn't support window functions (ROW_NUMBER()), you will need another way. One is to count all products from the same category with a smaller id. So you would need a subquery like
select count(*)
from products p2
where p2.category = p1.category
and p2.item is null
and p2.id <= p1.id
The final update query would be:
update products p
join (
select p1.id, ud.item
from (
select 0 as position, 0 as category, 0 as item from dual having item > 0
union all select 1, 2, 222
union all select 2, 2, 221
union all select 1, 3, 333
union all select 1, 5, 555
) as ud -- update data
join products p1
on p1.category = ud.category
and ud.position = (
select count(*)
from products p2
where p2.category = p1.category
and p2.item is null
and p2.id <= p1.id
)
where p1.item is null
) sub using (id)
set p.item = sub.item;
However I suggest first to try the simple way and execute one statement per row. It could be something like:
$stmt = $db->prepare("
update products
set item = ?
where category = ?
and item is null
order by id asc
limit 1
");
$db->beginTransaction();
foreach ($updateData as $category => $items) {
foreach($items as $item) {
$stmt->execute([$item, $category]);
if ($db->rowCount() == 0) {
break; // no more rows to update for this category
}
}
}
$db->commit();
I think the performance should be OK for like up to 1000 rows given an index on (category, item). Note that the single query solution might also be slow due to the expensive subquery to determine the position.
You cannot do what you want, because category is not unique in the table.
You can remove the current rows and insert the values that you really want:
delete p from products p
where category in (2, 3, 5) and itemid is null;
insert into product (itemid, category)
values ('222', '2' ), ('211', '2' ), ('333', '3' ), ('555', '5' );
MySQL can't make sense of your CASE expression.
You wrote
CASE
WHEN category = 2 THEN 222
WHEN category = 2 THEN 211
What do you want to happen when category is 2? You have provided two alternatives, and MySQL can't guess which one you want. That's your duplicate entry.
The error message is admittedly a little confusing.

Finding the latest updated value only from table

Hey guys i have a table structure like
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25) ,
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
)
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (1,'aff',2,3,5)
VALUES (2,'afff',1,31,52)
update CUSTOMERS
set NAME = 'somenewname'
where age = 1;
I just want to get output as 'somename'
I have tried creating a column updatedtime with timestamp and used query like
SELECT * FROM CUSTOMERS ORDER BY updatetime DESC LIMIT 1;
But this one shows the whole values ..
I just need only the updated value 'somenewname` as output.
I have also tried using triggers and functions but it didnt helped..
Please help me with a solution..I will accept it as an answer if it helps me ..remember i want an output as a single value like somenewname.
Thanx
The trigger which i have used
CREATE TRIGGER getrandom
AFTER UPDATE ON CUSTOMERS
FOR EACH ROW
BEGIN
UPDATE CUSTOMERS
SET NAME = 'anewname'
WHERE ADDRESS = 3;
END;
Why don't you try to SELECT only the name-column
SELECT name FROM CUSTOMERS ORDER BY updatetime DESC LIMIT 1; //outputs the name
instead of the whole result
SELECT * FROM CUSTOMERS ORDER BY updatetime DESC LIMIT 1;
Your UPDATE statement should look like
update CUSTOMERS
set NAME = 'somenewname',
updatetime = now()
where age = 1;
Now a example of how to get only the name if tables are joined.
SELECT c.name FROM CUSTOMERS as c INNER JOIN orders as o c.id=o.id ORDER BY c.updatetime DESC LIMIT 1;

Include duplicate results in MySQL result

I have a query of this form:
$query = "SELECT `id`, `value` FROM `table`
WHERE `id` IN "."('".$mod."') ORDER BY FIELD (`id`,'".$mod."')";
$mod is an Array of values that gets transformed into a string like this:
$mod = implode("','", $mod);
The values in $mod are simple non-negative integers but they sometimes repeat... I need the query to return a result that has all rows including duplicates in order i.e.
when $mod contains the following set of id's {35,21,34,14,35,21} the query should return these rows:
$result id value
row1 35 "value35"
row2 21 "value21"
row3 34 "value34"
row4 14 "value14"
row5 35 "value35"
row6 21 "value21"
You need a temporary table containing your set of id's. Use an incrementing primary key for the order.
CREATE TEMPORARY TABLE setofids ( setorder int auto_increment primary key, id int);
INSERT INTO setofids (id) VALUES (35),(21),(34),(14),(35),(21);
Then join to your table:
SELECT table.id, table.value FROM table
JOIN setofids USING (id)
ORDER BY setofids.setorder

MySQL Query Order by the query itself

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

Categories