I need to execute these two queries from php, is there a way to merge them together in a single query or I have to use a stored procedure?
SET #rn=0;
UPDATE `nl_emails` SET `row_num`=(#rn:=#rn+1);
Thanks in advance
It doesn't look like it is possible. We could create #rn in the query but it will be local and the value will be lost from one row to another.
Here is another way of doing the what I believe you want to do.
create table nl_emails (id int not null primary key ,row_num int);
insert into nl_emails values(10,10),(20,20),(30,30);
with cte as(
select id, row_num,
row_number() over (order by id)rn
from nl_emails)
update nl_emails join cte on nl_emails.id = cte.id
set nl_emails.row_num = rn;
select * from nl_emails;
id | row_num
-: | ------:
10 | 1
20 | 2
30 | 3
db<>fiddle here
So as question marked with php tag, you can use PHP PDO solution:
<?php
$sql = "SET #rn = 0;
UPDATE nl_emails SET row_num = (#rn:=coalesce(#rn, 0) + 1);";
$pdo->exec($sql);
Test PHP PDO online
You can make in one direct query, but you have to check the performance.
Use:
UPDATE `nl_emails` n1
INNER JOIN (
SELECT (#row_number:=#row_number + 1) AS num,
id
FROM nl_emails, (SELECT #row_number:=0) AS t
) as t1 on n1.id=t1.id
SET n1.`row_num`=t1.num;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=bf7d4a9243eed3c3e3aeb934846294b7
The key part is the cross join used
SELECT (#row_number:=#row_number + 1) AS num,
id
FROM nl_emails, (SELECT #row_number:=0) AS t
;
Related
This question already has an answer here:
Mysqli doesn't allow multiple queries?
(1 answer)
Closed 4 years ago.
I've looked at about a dozen posts about queries not working in PHP, but they all haven't been able to solve my problem - hopefully this is an easy one!
What I am attempting to do here is "rank" the rows based on their total yearly sales, and that's worked pretty well so far. When I run my query in MySQL, it works properly - I get back hundreds of results.
SET #row_number := 0;
SELECT #row_number := #row_number + 1 AS row_number,
TotalRevenue,
CompanyID
FROM (
SELECT CompanyID,
SUM(Sales_Amt) AS TotalRevenue
FROM Sales,
(SELECT #row_number := 0) r
GROUP BY CompanyID
) t
WHERE TotalRevenue > 0
ORDER BY TotalRevenue DESC
Produces:
row_number | TotalRevenue | CompanyID
-----------+--------------+----------
1 | 81130.00 | 333
2 | 72234.00 | 876
3 | 62653.00 | 123
4 | 54408.40 | 999
5 | 44548.00 | 111
However, when I run it via PHP, I get back the error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT #row_number := #row_number + 1 AS row_number,
TotalRevenue' at line 2
Based on other posts here, I've tried:
adding `` around my column names
adding '' around my column names
adding := instead of = when I created #row_number
confirming that all spaces are added in after my query in PHP so that key words are not squished together
setting my mysqli->charset to utf-8
Here's my PHP code, in case you need that as well:
$query = "SET #row_number := 0;
SELECT #row_number := #row_number + 1 AS `row_number`,
TotalRevenue,
CompanyID
FROM (
SELECT CompanyID,
SUM(`Sales_Amt`) AS TotalRevenue
FROM Sales
GROUP BY CompanyID
) t
WHERE TotalRevenue > 0
ORDER BY TotalRevenue DESC";
if (!$result = $mysqli->query($query))
{
print_r($mysqli->error);
}
Hoping this will be something really simple that I am just NOT seeing.
Thanks!
To add another option aside from S. Dev's answer, you can use mysqli multi query;
$query = "SET #row_number := 0;
SELECT #row_number := #row_number + 1 AS `row_number`,
TotalRevenue,
CompanyID
FROM (
SELECT CompanyID,
SUM(`Sales_Amt`) AS TotalRevenue
FROM Sales
GROUP BY CompanyID
) t
WHERE TotalRevenue > 0
ORDER BY TotalRevenue DESC";
if (!$result = $mysqli->multi_query($query))
{
print_r($mysqli->error);
}
This will allow you to preform more than 1 query at one time, keeping it all within the same remit of the query
It also uses slightly less overhead as you are only doing (technically) 1 query as there is only 1 connection rather than 2 separate calls
It could be worth looking into stored procedures to make it so you don't have to set variables, rather, pass them into the query itself
Duplicate this table: User_Posts
ID | Upvotes | Downvotes | CAT |
___________________________________
42134 | 5 | 3 | Blogs|
------------------------------------
12342 | 7 | 1 | Blogs|
-------------------------------------
19344 | 6 | 2 | Blogs|
------------------------------------
I need to get the rank of an item within it's category. Therefore ID: 19344 will have Rank position 2, with 4 upvotes, behind 12342 with 6 upvotes. Rank is determined by (upvotes-downvotes) count within it's category.
So I wrote this MySQL query.
SELECT rank FROM (SELECT *, #rownum:=#rownum + 1 AS rank
FROM User_Posts where CAT= 'Blogs' order by
(Upvotes-Downvotes) DESC) d,
(SELECT #rownum:=0) t2 WHERE POST_ID = '19344'
Returns to me (Rank = 2) when run directly in mysql. This is the correct result
However when I try to build it out through code-igniter's query builder I get the
$table = 'User_Posts';
$CAT= 'Blogs';
$POST_ID = '19344';
$sql = "SELECT rank FROM (SELECT *, #rownum:=#rownum + 1 AS
rank FROM $table where CAT= ?
order by (Upvotes-Downvotes) DESC) d,
(SELECT #rownum:=0) t2 WHERE POST_ID= ?";
$query= $this->db->query($sql, array($CAT,$POST_ID))->row_array();
returns to me an empty result: array(rank=>);
so then my question is... but why?
I will also accept an answer will an alternative way to run this query from code-igniters query builder, but ideally I would like to know why this thing is broken.
I've had a similar issue in the past, turns out I had to initialize the variable with a separate query first, I am not sure if this is still the case, but give it a try anyway.
//initialize the variable, before running the ranking query.
$this->db->query('SELECT 0 INTO #rownum');
$query= $this->db->query($sql, array($CAT,$POST_ID))->row_array();
Exactly I don't know why your code is not working. I wrote another solution it will work. Try below code.
$select="FIND_IN_SET( (upvote-downvote), (SELECT GROUP_CONCAT( (upvote-downvote) ORDER BY (upvote-downvote) DESC ) as total FROM (User_Posts))) as rank";
$this->db->select($select,FALSE);
$this->db->from('(User_Posts)',FALSE);
$this->db->where('ID',19344);
$this->db->where('CAT','Blogs');
$query = $this->db->get();
Write a Stored Function to do the query. Then have Codeigniter merely do
query("SELECT PostRank(?,?)", $CAT, $POST_ID);
Restriction: Since you cannot do PREPARE inside a Stored Function, this function will necessarily be specific to one table, User_Posts.
I'm not entirely sure if this is the problem, but I'd be initialising #rownum in the subquery:
SELECT rank
FROM (
SELECT *,
#rownum:=#rownum + 1 AS rank
FROM $table
JOIN (SELECT #rownum := 0) init
WHERE CAT= ?
ORDER BY (Upvotes-Downvotes) DESC
) d
WHERE post_id = ?
Otherwise I'd be worried that #rownum is undefined (NULL) and stays that way while rank is calculated (NULL + 1 = NULL), only being assigned the value of 0 afterwards. Thus rank is returned as NULL and you get ['rank'=>].
Running this again in a constant connection (directly in MySQL) would then give you the correct result as #rownum would start from the value 0 from the previous query and rank would be calculated correctly.
I'm guessing codeigniter starts a new connection/transaction each time the query is run and #rownum starts at NULL each time, giving ['rank'=>].
I need help to write MySQL query.
I have table full of logs where one of the column is unix timestamp.
I want to group (GROUP BY) those records so that events that were made in close range time (i.e. 5 sec) between each of them are in one group.
For example:
Table:
timestamp
----------
1429016966
1429016964
1429016963
1429016960
1429016958
1429016957
1429016950
1429016949
1429016943
1429016941
1429016940
1429016938
Become to groups like that:
GROUP_CONCAT(timestamp) | COUNT(*)
-----------------------------------------------------------------------------
1429016966,1429016964,1429016963,1429016960,1429016958,1429016957 | 6
1429016950,1429016949 | 2
1429016943,1429016941,1429016940,1429016938 | 4
Of course I can work with the data array afterwards in php, but I think that mysql would do it faster.
I started by using a variable to get the position of each row, where 1 is the highest time column and ending with the lowest, like this:
SET #a := 0;
SELECT timeCol, #a := #a + 1 AS position
FROM myTable
ORDER BY timeCol DESC;
For simplicity, we will call this positionsTable so that the rest of the query will be more readable. Once I created that table, I used a 'time_group' variable that checked if a previous row was within the last 5 seconds. If it was, we keep the same time_group. It sounds ugly, and looks kind of ugly, but it's like this:
SELECT m.timeCol, m.position,
CASE WHEN (SELECT p.timeCol FROM positionsTable p WHERE p.position = m.position - 1) <= m.timeCol + 5
THEN #time_group
ELSE #time_group := #time_group + 1 END AS timeGroup
FROM positionsTable m;
And then ultimately, using that as a subquery, you can group them:
SELECT GROUP_CONCAT(timeCol), COUNT(*)
FROM(
SELECT m.timeCol, m.position,
CASE WHEN (SELECT p.timeCol FROM positionsTable p WHERE p.position = m.position - 1) <= m.timeCol + 5
THEN #time_group
ELSE #time_group := #time_group + 1 END AS timeGroup
FROM positionsTable m) tmp
GROUP BY timeGroup;
Here is an SQL Fiddle example.
http://sqlfiddle.com/#!9/37d88/20
SELECT GROUP_CONCAT(t1.t) as `time`,
COUNT(*)
FROM (SELECT *
FROM table1
ORDER BY t) as t1
GROUP BY CASE WHEN (#start+5)>=t THEN #start
ELSE #start:=t END
I have a mysql table which looks something like this:
id_one id_two
1 2
2 1
3 2
2 3
4 5
5 4
I want to delete rows with two duplicate values inrespective of which columns they are in so the example would look like this:
id_one id_two
1 2
3 2
5 4
There are over 12 million rows in total. Any ideas on how I should do this?
Php or mysql query would be preferred.
DELETE a
FROM table1 a
LEFT JOIN
(
select id_one, id_two
from Table1
GROUP BY least(id_one, id_two), greatest(id_one, id_two)
) b ON a.id_one = b.id_one AND a.id_two = b.id_two
WHERE b.id_two IS NULL
SQLFiddle Demo
I would advise a 2-step approach:
Make id_one always the smaller value, i.e., if id_one is larger than id_two then swap their values - consider something like this (taken from here):
UPDATE tablename
SET id_one = (#temp:=id_one), id_one = id_two, id_two = #temp
WHERE id_one > id_two
Remove the duplicates as described here:
DELETE tablename FROM tablename INNER JOIN
(SELECT min(primary_key) AS min_id, id_one, id_two FROM tablename
GROUP BY id_one, id_two
HAVING count(1) > 1) AS d
ON (d.id_one = tablename.id_one
AND d.id_two = tablename.id_two
AND d.min_id <> tablename.primary_key)
(I assume that you will have a primary key on a table that holds 12 million entries.)
Not tested, so please backup your data!
DELETE FROM ztable zt
WHERE zt.id_one > zt.id_two
AND EXISTS (
SELECT *
FROM ztable tx
WHERE tx.id_one = zt.id_two
AND tx.id_two = zt.id_one
)
;
won't work in mysql, because in mysql you cannot reference the table being updated or deleted.
Since you want to make a backup copy anyway, you could use that instead in the EXISTS subquery:
CREATE table safetable AS (SELECT * from ztable);
DELETE FROM ztable zt
WHERE zt.id_one > zt.id_two
AND EXISTS (
SELECT *
FROM safetable tx
WHERE tx.id_one = zt.id_two
AND tx.id_two = zt.id_one
);
I have a table that is is sorted 1st by Reminder Date then ID
Table Looks like:
ID | remind_date
1 2011-01-23
2 2010-02-21
4 2011-04-04
5 2011-04-04
6 2009-05-04
I am using a PHP front end to move forward and back thur the records. I want to have forward and back buttons but i am running into a problem with the 2 reminder dates that are the same.
Just to note the ID's are NOT in order, they are here but in the actual database they are mixed up when sorting by reminder_date
The select statement i am using is: ($iid is the current record i am on)
SELECT id FROM myDB.reminders where remind_date > (SELECT remind_date FROM myDB.reminders where id=$iid) order by remind_date ASC LIMIT 1
So what happens when i get to the dates that are the same its skips over one because its asking for remind_date >.
If i use remind_date >= it returns the current record. My solution was then to use limit 2 and check via PHP to if the 1st record = my current ID, if it did use the next one. but what it there are 3 dates the same or 4 etc..
I also thought about using the ID field but since they are out of order i can't add in a ID > $iid.
Any ideas? it works great except for 2 dates that are the same.
You might be able to use this:
SELECT ID, remind_date
FROM
(
SELECT #prev_id := -1
) AS vars
STRAIGHT_JOIN
(
SELECT
ID,
remind_date,
#prev_id AS prev_id,
#prev_id := id
FROM myDB.reminders
ORDER BY remind_date, ID
) T1
WHERE prev_id = $iid
Here is a test of the above with your test data from your comment:
CREATE TABLE Table1 (ID INT NOT NULL, remind_date DATE NOT NULL);
INSERT INTO Table1 (ID, remind_date) VALUES
(45, '2011-01-14'),
(23, '2011-01-22'),
(48, '2011-01-23'),
(25, '2011-01-23'),
(63, '2011-02-19');
SELECT ID, remind_date
FROM
(
SELECT #prev_id := -1
) AS vars
STRAIGHT_JOIN
(
SELECT
ID,
remind_date,
#prev_id AS prev_id,
#prev_id := id
FROM table1
ORDER BY remind_date, ID
) T1
WHERE prev_id = 25
Result:
ID remind_date
48 2011-01-23
add a condition WHERE ID<>MY_LAST_ID. This can not work with triple and more same dates, so you can collect already taken ID's to array like (4,5,6) - see array_push(), implode it with "," to convert to a string (let's call it YOUR_IDS_STRING) and add to your query:
WHERE id NOT IN( YOUR_IDS_STRING )
And after each query make check, does date has changed and if it does - you can unset your array and start from begining (this is not neccesary, but gives you more performance, because YOUR_ID_STRING will be only that long as is need).
If your page is refreshing between queries, maybe try to push YOUR_ID_STRING in session variable, _GET or cookies, and simply concat next id's by operator .=
I used the code provided by Mark Byers and with small changes I adapted it to navigate in opposite directions (and to pass other columns too, not only the date and ID):
$results = $mysqli->query("SELECT * FROM (SELECT #prev_id := -1) AS vars STRAIGHT_JOIN (SELECT *, #prev_id AS prev_id, #prev_id := ID FROM my_table ORDER BY data, ID) T1 WHERE prev_id = ".$ID);
$results = $mysqli->query("SELECT * FROM (SELECT #next_id := 1) AS vars STRAIGHT_JOIN (SELECT *, #next_id AS next_id, #next_id := ID FROM my_table ORDER BY data DESC, ID DESC) T1 WHERE next_id = ".$ID);
I tested it on duplicate dates and it navigates well trough a list of records displayed with:
$results = $mysqli->query("SELECT * FROM my_table ORDER BY data DESC, ID DESC");