Pre-ordering a GROUP BY statement - mysql

This is a follow-up to a previous question: GROUP BY ordering.
I have the following sql table and data: http://sqlfiddle.com/#!9/81c3b6/2/0.
The basic un-ordered SQL statement is:
SELECT territory_id, platform_type_id, store_url
FROM main_itemmaster
GROUP BY platform_type_id
I want to get a single entry for each platform_type_id that is in the table, with a preference for territory_id='US'. That is, if an entry exists where territory_id='US', I would like to grab that one in the GROUP BY statement.
What would be the correct SQL statement to return the GROUPed by statement with a preference for the US item first?

In MySQL, the safest way to do this probably involves variables:
select im.*
from (select im.*,
(#rn := if(#p = platform_type_id, #rn + 1,
if(#p := platform_type_id, 1, 1)
)
) as rn
from main_itemmaster im cross join
(select #rn := 0, #p := '') params
order by platform_type_id, (territory_id = 'US') desc
) im
where rn = 1;
The does not involve using the MySQL (mis)feature that permits columns in the SELECT of an aggregation query that are not aggregated and not in the GROUP BY.
Here is a SQL Fiddle showing it working.
EDIT:
On the subject of the order of evaluation of variables. From the documentation:
As a general rule, other than in SET statements, you should never
assign a value to a user variable and read the value within the same
statement. For example, to increment a variable, this is okay:
SET #a = #a + 1;
For other statements, such as SELECT, you might get the results you
expect, but this is not guaranteed. In the following statement, you
might think that MySQL will evaluate #a first and then do an
assignment second:
SELECT #a, #a:=#a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
The above code does, technically, read the variable in the same statement, but it is also in the same expression. The semantics of if() (and case which I sometimes also use) guarantee the order of evaluation of the expressions.

Try that :
SELECT * FROM main_itemmaster
WHERE territory_id LIKE 'US'
GROUP BY platform_type_id
UNION
SELECT * FROM main_itemmaster
WHERE platform_type_id NOT IN (
SELECT platform_type_id FROM main_itemmaster
WHERE territory_id LIKE 'US')
GROUP BY platform_type_id

This should work
SELECT territory_id, platform_type_id, store_url
FROM main_itemmaster
GROUP BY platform_type_id
ORDER by if(territory_id ='US',1,99) asc
Here is the sqlfiddle for the above

Related

SQL: A column in a subquery does not appear

There is a query in MySQL 5.7:
SELECT * FROM
(
SELECT (#rowNum:=#rowNum+1) AS rowNo,t.* FROM table_target t,(SELECT (#rowNum :=0)) AS b
WHERE p_d = '2020-11-08'
ORDER BY kills DESC
) t
WHERE t.uid= '8888'
Running this query, there is no exception but column B disappears and if using select b from in the outter query, it returns unknown column exception.
I have 2 questions:
Why the (SELECT (#rowNum :=0)) does not appear?
Is the (#rowNum:=#rowNum+1) equivelent to row_number() over () in Oracle? If so, how to understand it...
Thanks for your help in advance.
In addition, I just found if I put the (SELECT (#rowNum :=0) ) in the left:
...
SELECT (SELECT (#rowNum :=0) ) AS b, (#rowNum:=#rowNum+1) AS rowNo , t.* FROM table_target t
...
Then the row number column does not increase any more, why could this happen?
You have asked 3 questions here:
Question 1: Why the (SELECT (#rowNum :=0)) does not appear?
Answer: You have used (SELECT (#rowNum :=0)) as B as a table joining it but not calling it in column list after select. That's why it is not showing it in output. You have called it as (#rowNum:=#rowNum+1) which is showing the value after increment means starting from 1.
Question 2: Is the (#rowNum:=#rowNum+1) equivalent to row_number() over () in Oracle? If so, how to understand it
Answer: Yes, it is equivalent. MySql 8.0 and above also support this (known as window function). It works as:
At the time of initialization of the query (SELECT (#rowNum :=0)) variable #rowNum will be initialized with value 0.
When we are calling (#rowNum:=#rowNum+1) in select then it will add 1 in #rowNum and assign it to itself for every row returned by select query.
This is how it will print the row number.
Question 3: if I put the (SELECT (#rowNum :=0) ) in the left:
Answer: If you put the (SELECT (#rowNum :=0) ) as field list after select then it will initialize the value of #rownum to 0 in every row returned by select. This is why you will not get incremented value.
The column "disappears" because the value is NULL. MySQL does not guarantee the order of evaluation of expressions in the SELECT, the initialization might not work.
Second, you code does not do what you intend, even if that worked, because variables may not respect the ORDER BY. I think you intend:
select k.*
from (select (#rownum := #rownumn + 1) as rownum, k.*
from (select k.*
from kills k
where k.p_d = '2020-11-08'
order by kills desc
) k cross join
(select #rownum := 0) params
) k
where t.uid = '8888';
There are probably better ways to do what you want. But your question is specifically about variables and MySQL 5.7.

How to fix an order column in MySQL?

Ok, so this is not the typical question on how to run a query with ASC or DESC. What I need to do is the following:
I have the following table:
MySQL Order.
I need to run a query that fixes the orders. In other words, that the values for order are modified to (1-10) correctly. The result of running such query would be the following table:
MySQL correct orders
What would be the best way to achieve this?
select id, #rank := #rank + 1 as new_order
from your_table
cross join (select #rank := 0) r
order by `order`

SQL - calculate MODE without sorting

I would like to calculate the MODE of a single column in SQL. This is done easily enough with:
SELECT v AS Mode
FROM Data
GROUP BY v HAVING COUNT(*) >= ALL (SELECT COUNT(*) FROM Data GROUP BY v);
However, I would like to do this without sorting, i.e. without using GROUP BY or any similar construct. Is there a quick and easy way to do this?
group by doesn't do sorting. It does partitioning. So instead of 1 aggregate result, you get 1 result per group in which all values that you group by are the same.
For MySQL the best I could come up with is this:
select distinct v from(
select v,
#cnt := (select count(*) from Data d1 where d1.v=d.v) as cnt_,
case when #cnt>=#max then #max:=#cnt end as max_
from Data d,
(select #max:=1, #cnt:=1) c) a
where cnt_ = #max
For SQL Server, Oracle or Postgres you can use a window function:
with a as (
v, select row_count() OVER(PARTITION BY v) rn
from Data
)
select v as Mode
FROM a
where rn = (select max(rn) from a)

#1221 - Incorrect usage of UPDATE and ORDER BY

To bypass a problem I posted in a other thread. I tried an sql statement like this:
UPDATE user u JOIN (SELECT #i := 0) r
SET user_rank_planets = (#i := (#i + 1))
WHERE user_active=1
ORDER BY user_planets DESC
I got Error #1221. Without the order by clause, the statement works fine.
Is there someone who knows a solution for this issue?
You cannot use order by and limit in update statement in the case of multiple tables.
Quoting From MySQL Documentation:
For the multiple-table syntax, UPDATE updates rows in each table named
in table_references that satisfy the conditions. Each matching row is
updated once, even if it matches the conditions multiple times. For
multiple-table syntax, ORDER BY and LIMIT cannot be used.
UPDATE user u
INNER JOIN
(
SELECT
*,
(#i := (#i + 1)) AS row_number
FROM user u
CROSS JOIN (SELECT #i := 0) r
WHERE user_active=1
ORDER BY user_planets DESC
)AS t
ON u.Primary_key = t.primary_key
SET u.user_rank_planets = t.row_number.
Note: Replace u.Primary_key and t.primary_key by the primary key of user table.
Read first few paragraphs http://dev.mysql.com/doc/refman/5.7/en/update.html
The #1000111's answer doesn't work. I don't know the reason but MySQL just ignored the ORDER BY in the subquery and updated with the default order (by the Primary_key).
A silly solution is wrapping the subquery inside another subquery to create a temporary table.
UPDATE user u
INNER JOIN
(
SELECT * FROM (
SELECT *, (#i := (#i + 1)) AS row_number
FROM user u
CROSS JOIN (SELECT #i := 0) r
WHERE user_active=1
ORDER BY user_planets DESC
) AS t1
) AS t
ON u.<Primary_key> = t.<Primary_key>
SET u.user_rank_planets = t.row_number

How to combine rows with same values in mysql

How do I combine rows with same values in the first row and put null or space in the rows instead without affecting GROUP BY subject in the select statement? Have a look at what I am trying to achieve and help me.
My attempted query is:
SELECT regd, GROUP_CONCAT(name order by name SEPARATOR ' ') as name,
subject, sc, fm FROM table GROUP BY regd, subject
Firstly, I would suggest that you handle this in code rather than at the DB level!
But, if you absolutely must do it all in a query, you could try ranking over partition with the regd column being the partition. Your expected output has rather arbitrarily ordered rows within each regd.
This query will order by subject within each regd:
select t.regd,
case when r=1 then t.name else null end as name,
t.subject,
t.sc,t.fm
from
(
select tt.*,
case when regd = #curRegd then #rank := #rank +1 else #rank:=1 end as r,
#curRegd := tt.regd
from table tt
join (SELECT #curRegd := 0,#rank:=0) r
order by regd,subject
) t
Finally, based on your stored data example, it seems like no aggregation i.e. GROUP BY clause, is necessary here.