SQL Switch even/odd values of the selection - mysql

Currently I have this selection from 2 tables:
(SELECT e.id, e.num, '1' as TBL
FROM events e
)
UNION ALL
(SELECT p.id, p.num, '2' as TBL
FROM places p
)
ORDER BY 2 DESC
It returns values ordered by num like this:
id | num | TBL
3 | 9 | 2
1 | 8 | 2
4 | 7 | 1
1 | 4 | 1
7 | 1 | 2
But my goal is to mix tables in selection not losing ORDER within a specific table. Like this:
id | num | TBL
3 | 9 | 2
4 | 7 | 1
1 | 8 | 2
1 | 4 | 1
7 | 1 | 2
Thanks in advance! I appreciate ANY help!

If you want to interleave the tables, you will need additional information. If you enumerate each row, then you can use that for sorting. Something like this:
(SELECT e.id, e.num, '1' as TBL, (#rn1 := #rn1 + 1) as rn
FROM events e CROSS JOIN
(SELECT #rn1 := 0) vars
ORDER BY e.num desc
)
UNION ALL
(SELECT p.id, p.num, '2' as TBL, (#rn2 := #rn2 + 1) as rn
FROM places p CROSS JOIN
(SELECT #rn2 := 0) vars
ORDER BY p.num desc
)
ORDER BY rn, tbl desc;

Try this:
DECLARE #t TABLE (rn INT IDENTITY,id INT,TBL INT)
INSERT INTO #t (id, TBL)
SELECT id,1 FROM events
INSERT INTO #t
SELECT id,2 FROM places
DECLARE #t2 TABLE (rn INT IDENTITY,num numeric(18,2))
INSERT INTO #t2 (num)
(
SELECT num
FROM events
UNION ALL
SELECT num
FROM places)
ORDER BY num DESC
SELECT id, num, TBL
FROM #t a
JOIN #t2 b ON a.rn = b.rn

Related

MySQL: Sequentially number a column based on change in a different column

If I have a table with the following columns and values, ordered by parent_id:
id parent_id line_no
-- --------- -------
1 2
2 2
3 2
4 3
5 4
6 4
And I want to populate line_no with a sequential number that starts over at 1 every time the value of parent_id changes:
id parent_id line_no
-- --------- -------
1 2 1
2 2 2
3 2 3
4 3 1
5 4 1
6 4 2
What would the query or sproc look like?
NOTE: I should point out that I only need to do this once. There's a new function in my PHP code that automatically creates the line_no every time a new record is added. I just need to update the records that already exist.
Most versions of MySQL do not support row_number(). So, you can do this using variables. But you have to be very careful. MySQL does not guarantee the order of evaluation of variables in the select, so a variable should not be assigned an referenced in different expressions.
So:
select t.*,
(#rn := if(#p = parent_id, #rn + 1,
if(#p := parent_id, 1, 1)
)
) as line_no
from (select t.* from t order by id) t cross join
(select #p := 0, #rn := 0) params;
The subquery to sort the table may not be necessary. Somewhere around version 5.7, this became necessary when using variables.
EDIT:
Updating with variables is fun. In this case, I would just use subqueries with the above:
update t join
(select t.*,
(#rn := if(#p = parent_id, #rn + 1,
if(#p := parent_id, 1, 1)
)
) as new_line_no
from (select t.* from t order by id) t cross join
(select #p := 0, #rn := 0) params
) tt
on t.id = tt.id
set t.line_no = tt.new_line_no;
Or, a little more old school...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,parent_id INT NOT NULL
);
INSERT INTO my_table VALUES
(1, 2),
(2 , 2),
(3 , 2),
(4 , 3),
(5 , 4),
(6 , 4);
SELECT x.*
, CASE WHEN #prev = parent_id THEN #i := #i+1 ELSE #i := 1 END i
, #prev := parent_id prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY parent_id,id;
+----+-----------+------+------+
| id | parent_id | i | prev |
+----+-----------+------+------+
| 1 | 2 | 1 | 2 |
| 2 | 2 | 2 | 2 |
| 3 | 2 | 3 | 2 |
| 4 | 3 | 1 | 3 |
| 5 | 4 | 1 | 4 |
| 6 | 4 | 2 | 4 |
+----+-----------+------+------+
You can use subquery if the row_number() doesn't help :
select t.*,
(select count(*)
from table t1
where t1.parent_id = t.parent_id and t1.id <= t.id
) as line_no
from table t;

SQL query for the latest grade of employees which each employee have several records having same id

How to find the latest grade of an employee has the same id.
my table is like:
+----+-------+-------+
| ID | NAME | GRADE |
| 1 | ANN | A0 |
| 1 | ANN | A3 |
| 2 | JOE | B1 |
| 3 | KIM | B3 |
| 2 | JOE | B2 |
| 3 | KIM | C1 |
| 3 | KIM | C3 |
+----+-------+-------+
How to find the latest grade of ann, Kim, and joe
my output is like:
name latestgrade
ann A3
joe B2
kim C3
Give a row number group by ID and order by the descending order of Grade
Sql Server Query
;with cte as(
select [rn] = row_number() over(
partition by [ID]
order by len([GRADE]) desc, [GRADE] desc
), *
from [your_table_name]
)
select [ID], [NAME], [GRADE]
from cte
where [rn] = 1;
demo
MySql Query
select t1.`ID`, t1.`NAMME`, t1.`GRADE` from (
select `ID`, `NAME`, `GRADE`, (
case `ID` when #curA
then #curRow := #curRow + 1
else #curRow := 1 and #curA := `ID` end
) as `rn`
from `your_table_name` t,
(select #curRow := 0, #curA := '') r
order by `ID`, length(`GRADE`) desc, `GRADE` desc
)t1
where t1.`rn` = 1;
demo
try row_number
;with cte
as
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY
SUBSTRING(GRADE,1,1) desc,CAST(SUBSTRING(GRADE,2,LEN(GRADE)) AS INT) DESC),
*
FROM YourTable
)
SELECT
*
FROM CTE
WHERE SeqNo = 1
If you wish to insert the above result into a new table, just write a insert into before the select
;with cte
as
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY
SUBSTRING(GRADE,1,1) desc,CAST(SUBSTRING(GRADE,2,LEN(GRADE)) AS INT) DESC),
*
FROM YourTable
)
insert into MyNewTable
SELECT
*
FROM CTE
WHERE SeqNo = 1

Get first date from timestamp in SQL

I have in my Moodle db table for every session sessid and timestart. The table looks like this:
+----+--------+------------+
| id | sessid | timestart |
+----+--------+------------+
| 1 | 3 | 1456819200 |
| 2 | 3 | 1465887600 |
| 3 | 3 | 1459839600 |
| 4 | 2 | 1457940600 |
| 5 | 2 | 1460529000 |
+----+--------+------------+
How to get for every session the first date from the timestamps in SQL?
You can easy use this:
select sessid,min(timestart) FROM mytable GROUP by sessid;
And for your second question, something like this:
SELECT
my.id,
my.sessid,
IF(my.timestart = m.timestart, 'yes', 'NO' ) AS First,
my.timestart
FROM mytable my
LEFT JOIN
(
SELECT sessid,min(timestart) AS timestart FROM mytable GROUP BY sessid
) AS m ON m.sessid = my.sessid;
Try this.
SELECT
*
FROM
tbl
WHERE
(sessid, timestart) IN (
SELECT tbl2.sessid, MIN(tbl2.timestart)
FROM tbl tbl2
WHERE tbl.sessid = tbl2.sessid
);
Query
select sessid, min(timestart) as timestart
from your_table_name
group by sessid;
Just an other perspective if you need even the id.
select t.id, t.sessid, t.timestart from
(
select id, sessid, timestart,
(
case sessid when #curA
then #curRow := #curRow + 1
else #curRow := 1 and #curA := sessid end
) as rn
from your_table_name t,
(select #curRow := 0, #curA := '') r
order by sessid,id
)t
where t.rn = 1;

SQL Reverse entire column

is there a way to reverse an entire column?
Example:
ID ColX ColY ColZ
0 001 010 100
1 002 020 200
2 003 030 300
shall be:
ID ColX ColY ColZ
0 003 030 300
1 002 020 200
2 001 010 100
So the Column ID shall be reversed, the record with the last ID shall be the first, the second last the second first and so far.
The newest value has ID = 0 and the oldest ID = n, and this must be exactly reversed, else I cannot insert new records.
You can do it using variables:
SELECT t2.ID, ColX, ColY, ColZ
FROM (SELECT ID, ColX, ColY, ColZ,
#row_number := #row_number + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #row_number := 0) AS var
ORDER BY ID) AS t1
INNER JOIN (
SELECT ID, #rn := #rn + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0) AS var
ORDER BY ID DESC) AS t2
ON t1.rn = t2.rn
ORDER BY t2.ID
Demo here
If you want to UPDATE then you can use the above query in an UPDATE statement like this:
UPDATE mytable AS t
INNER JOIN(
SELECT ID, ColX, ColY, ColZ,
#row_number := #row_number + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #row_number := 0) AS var
ORDER BY ID) AS t1 ON t.ID = t1.ID
INNER JOIN (
SELECT ID, #rn := #rn + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0) AS var
ORDER BY ID DESC) AS t2 ON t1.rn = t2.rn
SET t.ID = t2.ID
Demo here
The above will work irrespective of the values of ID column.
If you don't have gaps in your ID, then you can use this select query:
SELECT
max_id-ID AS ID,
ColX,
ColY,
ColZ
FROM
mytable CROSS JOIN (SELECT MAX(ID) AS max_id FROM mytable) m
ORDER BY
ID
or this update query (but it will work only if ID is not a primary key):
UPDATE
mytable m1 CROSS JOIN (SELECT MAX(ID) as max_id FROM mytable) m
SET
m1.ID = m.max_id - m1.ID
if it's a primary key you could use this:
UPDATE
mytable m1 CROSS JOIN (SELECT MAX(ID) as max_id FROM mytable) m
INNER JOIN mytable m2 ON m1.ID = m.max_id - m2.ID
SET
m1.ColX = m2.ColX,
m1.ColY = m2.ColY,
m1.ColZ = m2.ColZ
(please see it here)
but if your ID column has gaps (e.g. 0, 1, 2, 5, 6) you need a different approach.
SELECT
#x - t.id AS new_id,
t.*
FROM tab t,
( SELECT #x:= max(id+1) FROM tab ) m;
MariaDB [tmp]> SELECT
-> #x - t.id AS new_id,
-> t.*
-> FROM tab t,
-> ( SELECT #x:= max(id+1) FROM tab ) m;
+--------+----+--------+---------------------+
| new_id | id | action | date |
+--------+----+--------+---------------------+
| 7 | 1 | 2 | 2015-09-24 15:28:30 |
| 6 | 2 | 4 | 2015-09-24 15:29:26 |
| 5 | 3 | 2 | 2015-09-24 15:30:01 |
| 4 | 4 | 3 | 2015-09-24 15:30:55 |
| 3 | 5 | 1 | 2015-09-24 16:07:25 |
| 2 | 6 | 5 | 2015-09-24 16:10:25 |
| 1 | 7 | 4 | 2015-09-24 16:29:26 |
+--------+----+--------+---------------------+
7 rows in set (0.00 sec)

Assign the same rank if multiple users have same count value

i have user_points Table with two columns.
select user_values, userid from user_points, based on count of userid i want to assgin the rank to users.. i have write this query
SELECT count_temp.* , #curRank:=(#curRank + 1) AS rank
FROM (
SELECT userid, COUNT(*) AS totalcount FROM user_points t GROUP BY t.userid
) AS count_temp
, (SELECT #curRank := 0) r
ORDER BY totalcount DESC;
gives the result as :
userid | totalcount | rank
2 6 1
3 2 2
4 2 3
1 1 4
but i want to assgin to rank 2 for userid 3 and 4 because their totalcount are same ..
To emulate RANK() function, which returns the rank of each row within the partition of a result set, you can do
SELECT userid, totalcount, rank
FROM
(
SELECT userid, totalcount,
#n := #n + 1, #r := IF(#c = totalcount, #r, #n) rank, #c := totalcount
FROM
(
SELECT userid, COUNT(*) AS totalcount
FROM user_points t
GROUP BY t.userid
ORDER BY totalcount DESC
) t CROSS JOIN
(
SELECT #r := 0, #n := 0, #c := NULL
) i
) q;
Output:
| USERID | TOTALCOUNT | RANK |
|--------|------------|------|
| 2 | 6 | 1 |
| 3 | 2 | 2 |
| 4 | 2 | 2 |
| 1 | 1 | 4 |
To emulate DENSE_RANK() function, which returns the rank of rows within the partition of a result set, without any gaps in the ranking, you can do
SELECT userid, totalcount, rank
FROM
(
SELECT userid, totalcount,
#r := IF(#c = totalcount, #r, #r + 1) rank, #c := totalcount
FROM
(
SELECT userid, COUNT(*) AS totalcount
FROM user_points t
GROUP BY t.userid
ORDER BY totalcount DESC
) t CROSS JOIN
(
SELECT #r := 0, #c := NULL
) i
) q;
Output:
| USERID | TOTALCOUNT | RANK |
|--------|------------|------|
| 2 | 6 | 1 |
| 3 | 2 | 2 |
| 4 | 2 | 2 |
| 1 | 1 | 3 |
Here is SQLFiddle demo for both queries