I have a mysql table like this:
+----+------+
| id | rank |
+----+------+
| 1 | 2 |
+----+------+
| 2 | -1 |
+----+------+
| 3 | 5 |
+----+------+
| 4 | 1 |
+----+------+
| 5 | -1 |
+----+------+
| 6 | -1 |
+----+------+
| 7 | 8 |
+----+------+
| 8 | -1 |
+----+------+
Now I want to get the ids in the following order: At first
WHERE rank >= 1 ORDER BY rank ASC
and afterwards:
WHERE rank = -1
How can I get this in only one mysql_query()?
Try something like:
SELECT *
FROM mytable
WHERE rank >= 1
ORDER BY rank
UNION
SELECT *
FROM mytable
WHERE rank = -1
OR something like:
SELECT *
FROM mytable
WHERE rank >= 1
ORDER BY CASE WHEN rank>=1
THEN 0
ELSE 1,rank
Proposed answer:
SELECT id FROM myTable WHERE rank >= 1 ORDER BY rank ASC
UNION
SELECT id FROM myTable WHERE rank = -1
From my understanding, you wanted a column of id's, starting with the ids WHERE rank >= 1 ORDER BY rank ASC and ending with the ids WHERE rank = -1.
The previous sql query uses UNION which joins two resulting tables from separate SELECT queries. UNION can only be applied when you have the same mount of generated columns from each SELECT query, so that's a good thought to keep in mind if later you want to increase the amount of columns obtained.
You can also map rank with ELT.
sample
SELECT *
FROM mytable
ORDER BY ELT(rank+2,99,0,1,2,3,4,5,6,7,8) ASC;
Related
I would like to query a table, and update the table data, but the data more than 10000 row, so what is the effective way? My code now:
+----+------+---------+
|id | rank | newrank |
+----------------------+
|1 | 1 | 0 |
|2.. | 2 | 0 |
|10000| 10000| 0 |
+----------------------+
//I am using a template language, DB::query = mysqli_query
$ss = DB::fetch_all("SELECT *FROM t1 ORDER BY rank ASC");
$x = 1;
foreach($ss as $sr){
DB::query("UPDATE t1 SET newrank = '".$x."' WHERE id = '".$sr['id']."'");
$x++;
}
Currently I am using the above code, but if data have to update in huge amount, the server will be crashed. Thank you.
You don't need to fetch anything to do this, and you don't need to worry about the number of rows.*
Given a test table:
create table t1 (id int primary key, `rank` int);
insert into t1 values (1, null), (2, null), (3, null), (6, null);
Solution for MySQL 5.x:
set #r = 0;
update t1 set `rank` = (#r := #r + 1) order by `rank`, id;
select * from t1;
+----+------+
| id | rank |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 6 | 4 |
+----+------+
Here's a better solution for MySQL 8.0 using a window function. I'll rank them in the descending order, just so we can see that it changed the ranks:
with cte as ( select id, rank() over (order by `rank` desc, id desc) as r from t1 )
update cte join t1 using (id) set t1.`rank` = cte.r;
select * from t1;
+----+------+
| id | rank |
+----+------+
| 1 | 4 |
| 2 | 3 |
| 3 | 2 |
| 6 | 1 |
+----+------+
Note: I had to delimit rank with back-ticks because rank is a reserved keyword in MSQL 8.0.
* Eventually, you do need to worry about the number of rows. MySQL has a limit on the total size of a transaction, if the binary log is enabled. The total size of rows modified during a transaction is limited by the option max_binlog_cache_size. Not the number of rows, but the size of the rows in bytes.
If I have a table like this:
ID | ident | product
1 | cucu1 | 99867 |
2 | kkju7 | 88987 |
3 | sjdu4 | 66754 |
4 | kjhu6 | 76654 |
5 | cucu1 | 98876 |
And use this query: SELECT ident,COUNT(*) FROM sales WHERE status=? AND team=? AND DATE(date) = DATE(NOW() - INTERVAL 1 DAY) GROUP BY ident order by COUNT(*) DESC LIMIT 1
I get the value: cucu1, since that has the most rows.
But if my table is like this:
ID | ident | product
1 | cucu1 | 99867 |
2 | kkju7 | 88987 |
3 | sjdu4 | 66754 |
4 | kkju7 | 76654 |
5 | cucu1 | 98876 |
It should return both cucu1 and kkju7, since they are the highest with same count, but still it gives me only cucu1. What am I doing wrong?
You can use rank():
SELECT ident, cnt
FROM (SELECT ident, COUNT(*) as cnt,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM sales
WHERE status = ? AND team = ? AND
DATE(date) = DATE(NOW() - INTERVAL 1 DAY)
GROUP BY ident
) i
WHERE seqnum = 1;
LIMIT keyword simply limits the results to 1 row, no matter if there are equal values before or after the returned row.
A better solution would be to select the rows which have a count that's equal to max count in the query, which can be achieved by the following query:
SELECT ident,COUNT(*)
FROM sales
WHERE status=?
AND team=?
AND DATE(date) = DATE(NOW() - INTERVAL 1 DAY)
GROUP BY ident
HAVING COUNT(*) = MAX(COUNT(*))
I have a huge number of rows that I'd like to get say, last 5 records inserted in that database from 10 different users. If the same user inserted the last 3 rows into database, we must get one row, skip the others two and move to get a row per user, until it count up to 5.
A database like that:
user_id | news_id | title
1 | 1 | foo-1
2 | 2 | foo-2
3 | 3 | foo-3
1 | 4 | baa
4 | 5 | baa0
5 | 6 | baa1
5 | 7 | baa2
6 | 8 | baa3
7 | 9 | baa4
Should return:
user_id | news_id | title
1 | 1 | foo-1
2 | 2 | foo-2
3 | 3 | foo-3
4 | 5 | baa0
5 | 6 | baa1
The current filter was done by PHP, like this:
$used = array();
while ($data = mysql_fetch_array($query)) {
$uid = $data['user_id'];
if(in_array($uid, $used))
continue;
array_push($used, $uid);
// do something with data
}
But I want to refactor it, and do the filter purely by mysql, if possible. I don't know much MySql and that's why I'm having problem to archive this...
Here's what I've tried
select DISTINCT(user_id), news_id, title from XXX
WHERE GROUP BY (news_id) DESC
LIMIT 0,5
How can I do that?
1 way you can do it is to generate a partitioned row number per user and then select 5 records where RowNumber = 1.
SELECT *
FROM
(
SELECT
d.user_id
,d.news_id
,d.title
,(#rn:= if(#uid = user_id, #rn + 1,
if(#uid:=user_id,1,1)
)
) as RowNumber
FROM
Data d
CROSS JOIN (SELECT #uid:=-1, #rn:=0) vars
ORDER BY
user_id
,news_id
) t
WHERE
t.RowNumber = 1
ORDER BY news_id
LIMIT 5;
http://rextester.com/JRIZI7402 - example to show it working
Note you can change the row order by simply changing the ORDER BY statement of the derived table so if you have a column that will signify the latest record e.g. an identity column or a datetime column you can use that, but user_id must be the first criteria to be partitioned correctly.
Do it from your query.
"SELECT * FROM table GROUP BY user_id ORDER BY news_id DESC LIMIT 5"
well, i think this will achieve what you are after.
select user_id, news_id, title from tableName
GROUP BY user_id
ORDER BY news_id DESC
LIMIT 0,5
Hope this helps!
I have a table like
+------+----------+
| id | location |
+------+----------+
| 1 | TVM |
| 2 | KLM |
| 3 | EKM |
+------+----------+
And I have an array of id like [1,2,1,3,1]. I need to get the result as
+------+----------+
| id | location |
+------+----------+
| 1 | TVM |
| 2 | KLM |
| 1 | TVM |
| 3 | EKM |
| 1 | TVM |
+------+----------+
I am already tried WHERE IN like conditions but no luck.
A where statement cannot multiply the number of rows. It can only filter rows out. You want a join:
select tl.*
from tablelike tl join
(select 1 as n union all select 2 union all select 1 union all
select 3 union all select 1
) n
on tl.id = n.n;
Note: if you are already generating the list via a query or from a table, then use that for the query rather than passing the list out of the database and then back in.
You could also return this result with a query like this; this uses a separate SELECT to return each occurrence of row with id=1.
( SELECT id, location FROM mytable WHERE id IN (1,2)
ORDER BY id
)
UNION ALL
( SELECT id, location FROM mytable WHERE id IN (1,3)
ORDER BY id
)
UNION ALL
( SELECT id, location FROM mytable WHERE id IN (1)
ORDER BY id
)
Following a similar pattern, the result could be obtained by combining the results from five SELECT statements, each returning a separate row. That would probably be a little simpler to achieve from a small array, e.g.
$glue = ") ) UNION ALL
(SELECT id, location FROM mytable WHERE id IN (";
$sql = "(SELECT id, location FROM mytable WHERE id IN ("
. implode($glue, $myarray)
. ") )";
I want sum values into my database by the same ID in the same table.
Table in database:
| ID | Value_o | Value_t | Value_tt |
| 1 | 40 | 20 | 10 |
query:
SELECT SUM(Value_o) AS Value_o, SUM(Value_t) AS Value_t, SUM(Value_tt) AS Value_TT
WHERE ID IN(1, 1)
And now the output id:
| Value_o | Value_t | Value_tt |
| 40 | 20 | 10 |
but I want:
| Value_o | Value_t | Value_tt |
| 80 | 40 | 20 |
I want get this output without JOIN.
Thanks!
PS. Sorry for my bad eng :/
Maybe this is what you are looking for:
SELECT
SUM(Value_o) AS Value_o,
SUM(Value_t) AS Value_t,
SUM(Value_tt) AS Value_TT
FROM
(
SELECT ID, Value_o, Value_t, Value_tt FROM Table1
UNION ALL
SELECT ID, Value_o, Value_t, Value_tt FROM Table1
) Table2
WHERE ID IN(1, 1);
Demo
The MySQL in operator doesn't work this way. Even if you have a value multiple times in the set, it doesn't duplicate the rows of your result.
If you want to have all the rows multiple times, you must use union all and sum over that
SELECT SUM(Value_o) AS Value_o, SUM(Value_t) AS Value_t, SUM(Value_tt) AS Value_TT
from (select * from mytable union all select * from mytable) t
WHERE ID IN (1)
Try this:
SELECT SUM(Value_o) AS Value_o, SUM(Value_t) AS Value_t, SUM(Value_tt) AS Value_TT
FROM TABLE
GROUP BY ID
HAVING ID = 1