wrong order in SQL using PDO - mysql

I have this query, which will be stored in a variable named $query (the actual query is much more complex, but this example successfully addresses my problem):
select * from
(
select
id_field as 'id',
date_field as 'date',
name_field as 'name',
cast((#counter := #counter + 1) AS SIGNED) as 'counter'
from my_table
cross join (select #counter := 0) as tmp
) as x
At some point, the final query will be:
$final_sql = "select * from ($query) t order by date desc"; //this can't be changed
What's happening is that I'm not getting the rows ordered by date, instead I'm getting some random order.
I've narrowed the problem to the double select * from while running the query with php/pdo on the server (localhost works fine). If I run the raw sql directly in database (localhost or server) it also works fine
If $query was like this, then I get the correct order (select * from removed):
select
id_field as 'id',
date_field as 'date',
name_field as 'name',
cast((#counter := #counter + 1) AS SIGNED) as 'counter'
from my_table
cross join (select #counter := 0) as tmp
But I've also noticed that if I keep the double select * from and get rid of the variables, then it will work as expected (but I need that column so it's not an option)
select * from
(
select
id_field as 'id',
date_field as 'date',
name_field as 'name'
from my_table
) as x
So, what is going on here?! How can I make it work on my server, just like my localhost?
localhost:
db > MySQL 8.0.25
php > 7.3.27
pdo > mysqlnd 5.0.12-dev - 20150407
server:
db > MariaDB 10.1.44
php > 7.3.17
pdo > mysqlnd 5.0.12-dev - 20150407

By nature the resultset of subquery are unsorted, while mysql keeps it mariadb skips it
What you can do is making a LIMIT with an ORDER BY so that mysql and mariadb keops the order you need
select * from
(
select
id_field as 'id',
date_field as 'date',
name_field as 'name',
cast((#counter := #counter + 1) AS SIGNED) as 'counter'
from my_table
cross join (select #counter := 0) as tmp
ORDER BY date_field
LIMIT 18446744073709551615
) as x

Related

Getting Lost Connection on a Specific Query

I'm getting a 2013 Lost Connection to MySQL server During Query on MySQL hosted on an AWS instance. The query below is the only one that causes this error (other queries run fine), and this query also runs with no issues on my Synology Docker container running MySQL. The only unique thing I have identified is that this query uses a CTE and the others that run successfully do not.
The AWS MySQL is 8.0.23 and the NAS Docker MySQL is 8.0.28.
I have checked the first things such as max connections, timeouts, etc and the values in use for the AWS instance are the same or higher than the settings on the NAS Docker instance. I have also tried a smaller data table and reorganizing the data table to weed out the possibility of data corruption.
I have been searching for a couple of days and have not been able to find any hints on what the issue is.
Does anyone here have any suggestions about where I should look next? Thanks!
USE ce_test;
SET #lowlim = 0;
SET #upplim = 0;
with orderedList AS (
SELECT
576_VMC_Sol_Savings_Pct,
ROW_NUMBER() OVER (ORDER BY 576_VMC_Sol_Savings_Pct) AS row_n
FROM vmctco
),
quartile_breaks AS (
SELECT
576_VMC_Sol_Savings_Pct,
(
SELECT 576_VMC_Sol_Savings_Pct AS quartile_break
FROM orderedList
WHERE row_n = FLOOR((SELECT COUNT(*) FROM vmctco)*0.75)
) AS q_three_lower,
(
SELECT 576_VMC_Sol_Savings_Pct AS quartile_break
FROM orderedList
WHERE row_n = FLOOR((SELECT COUNT(*) FROM vmctco)*0.75) + 1
) AS q_three_upper,
(
SELECT 576_VMC_Sol_Savings_Pct AS quartile_break
FROM orderedList
WHERE row_n = FLOOR((SELECT COUNT(*) FROM vmctco)*0.25)
) AS q_one_lower,
(
SELECT 576_VMC_Sol_Savings_Pct AS quartile_break
FROM orderedList
WHERE row_n = FLOOR((SELECT COUNT(*) FROM vmctco)*0.25) + 1
) AS q_one_upper
FROM orderedList
),
iqr AS (
SELECT
576_VMC_Sol_Savings_Pct,
(
(SELECT MAX(q_three_lower)
FROM quartile_breaks) +
(SELECT MAX(q_three_upper)
FROM quartile_breaks)
)/2 AS q_three,
(
(SELECT MAX(q_one_lower)
FROM quartile_breaks) +
(SELECT MAX(q_one_upper)
FROM quartile_breaks)
)/2 AS q_one,
1.5 * ((
(SELECT MAX(q_three_lower)
FROM quartile_breaks) +
(SELECT MAX(q_three_upper)
FROM quartile_breaks)
)/2 - (
(SELECT MAX(q_one_lower)
FROM quartile_breaks) +
(SELECT MAX(q_one_upper)
FROM quartile_breaks)
)/2) AS outlier_range
FROM quartile_breaks
)
SELECT MAX(q_one) OVER () - MAX(outlier_range) OVER () AS lower_limit,
MAX(q_three) OVER () + MAX(outlier_range) OVER () AS upper_limit
INTO #lowlim, #upplim
FROM iqr
LIMIT 1;
SELECT #lowlim, #upplim;
SOLVED: I went ahead and updated the version on the AWS instance to the same version as the NAS (8.0.28) and the query runs correctly now.

The same SQL but have different result

ps:the description show detail with the href: https://segmentfault.com/q/1010000014649728
I run the follow sql :
select * from
(
select *,(#num2 :=
(if(#GROUP199=`C_ISHOT`,#num2+1,if(#GROUP199:=`C_ISHOT`,1,1)))) row_number
from city_code order by C_ISHOT
) result
where row_number<=10
the first run result show the table total numbers,
but the second run result is my expected。 what's the matter with it,and how can I resolve that?
The variables are not initialized.
select *
from ( select *, (#num2 :=(if(#GROUP199=C_ISHOT,#num2+1,if(#GROUP199:=C_ISHOT,1,1)))) row_number
from city_code, ( SELECT #num2 := 0, #GROUP199 := 0 ) init_vars
order by C_ISHOT ) result
where row_number<=10

using row number in update query - MySQL

I have this DB2 query which I want to make MySQL compliant :
UPDATE
(
SELECT x.name, row_number() over () as rown from XYZ x where x.id = '123' and
x.div='abc')A
SET
A.name = 'name_1'
where
A.rown<= ( select count(*) -1 from XYZ where id='123' and div='abc');
Now, I tried writing this I MySQL:
UPDATE
(
select x.name, (#row_number := #row_number +1) as rown
from XYZ x, (Select #row_number := 0)as t
where x.id='123' and x.div='abc'
) A
Set
A.name = 'name_1'
where
A.rown<= ( select count(*) -1 from XYZ where id='123' and div='abc');
However, it gives me the error: The target table A of the UPDATE is not updatable
I have tried multiple ways but all in vain. Where am I going wrong?
Also if the DB2 query can be made into MySql in any other way, since Mysql doesn't support
row_number()
You can't update a derived table. You need to join with the real table so you can update it.
UPDATE XYZ AS x
JOIN (
select x.id, (#row_number := #row_number +1) as rown
from XYZ x, (Select #row_number := 0) as t
where x.id='123' and x.div='abc'
) AS A ON x.id = A.id
Set X.name = 'name_1'
where A.rown <= ( select count(*) from XYZ where id='123' and div='abc');
I'm not sure if this will do the same thing as the DB2 query, though. It seems to assume some inherent ordering in the table, and perhaps DB2 provides such a thing, but MySQL doesn't make any guarantees about ordering when you don't use ORDER BY. If you add ORDER BY x.id in the subquery, maybe that will do what you want.
In DB2 you can do it :
update XYZ f1
set f1.name='name_1'
where f1.id='123' and f1.div='abc'
and rrn(f1) not in
(
select max(rrn(f2)) from XYZ f2 where f2.id='123' and f2.div='abc'
)

Mysql if statement to select multiple rows

I need to retrieve a set of data if a condition is true, and a set of other ata if the condition is false.
I searched in mysql manual and I tested the select case when statement, but as my subqueries return multiple rows, I'm not able to use it.
Is it a simple way to write a query with if statement? (without using stored procedure)
Here is my condition:
select case when ( right(from_unixtime(300 * floor(unix_timestamp(now())/300)) - INTERVAL 10 MINUTE, 8) = '23:50:00' ) then ( select SERVER_KEY from OVERSERVER ) else ( select SERVER_KEY from SERVER ) end;
Thanks
try something like this:
select SERVER_KEY from OVERSERVER
where right(from_unixtime(300 * floor(unix_timestamp(now())/300)) - INTERVAL 10 MINUTE, 8) = '23:50:00'
union all
select SERVER_KEY from SERVER
where right(from_unixtime(300 * floor(unix_timestamp(now())/300)) - INTERVAL 10 MINUTE, 8) != '23:50:00'
I am not sure but just give a try this-
select SERVER_KEY from (case when ( right(from_unixtime(300 * floor(unix_timestamp(now())/300)) - INTERVAL 10 MINUTE, 8) = '23:50:00' ) then "OVERSERVER" else "SERVER"
OR
select SERVER_KEY from (case when ( right(from_unixtime(300 * floor(unix_timestamp(now())/300)) - INTERVAL 10 MINUTE, 8) = '23:50:00' ) then OVERSERVER else SERVER
Note: above query is not tested.

Getting latest rows in MySQL based on date (grouped by another column)

This type of question is asked every now and then. The queries provided works, but it affects performance.
I have tried the JOIN method:
SELECT *
FROM nbk_tabl
INNER JOIN (
SELECT ITEM_NO, MAX(REF_DATE) as LDATE
FROM nbk_tabl
GROUP BY ITEM_NO) nbk2
ON nbk_tabl.REF_DATE = nbk2.LDATE
AND nbk_tabl.ITEM_NO = nbk2.ITEM_NO
And the tuple one (way slower):
SELECT *
FROM nbk_tabl
WHERE REF_DATE IN (
SELECT MAX(REF_DATE)
FROM nbk_tabl
GROUP BY ITEM_NO
)
Is there any other performance friendly way of doing this?
EDIT: To be clear, I'm applying this to a table with thousands of rows.
Yes, there is a faster way.
select *
from nbk_table
order by ref_date desc
limit <n>
Where is the number of rows that you want to return.
Hold on. I see you are trying to do this for a particular item. You might try this:
select *
from nbk_table n
where ref_date = (select max(ref_date) from nbk_table n2 where n.item_no = n2.item_no)
It might optimize better than the "in" version.
Also in MySQL you can use user variables (Suppose nbk_tabl.Item_no<>0):
select *
from (
select nbk_tabl.*,
#i := if(#ITEM_NO = ITEM_NO, #i + 1, 1) as row_num,
#ITEM_NO := ITEM_NO as t_itemNo
from nbk_tabl,(select #i := 0, #ITEM_NO := 0) t
order by Item_no, REF_DATE DESC
) as x where x.row_num = 1;