How to select value from first and second row - mysql

I want to select data from table
suppose we have a table
table Temp
sequence_number | breakdown_number | physical_account | logical_account | debit_amount | credit_amount
----------------+------------------+------------------+-----------------+--------------+---------------
1 | 1 | 10001 | 10 | 0
2 | 1 | 0011 | 10 | 0
Now I have to select physical_account from 1st row and logical account from second row and insert it into another table in single row based on the breakdown number.
How can I do this ?

I am going to assume that sequence_number actually provides the ordering of the rows and you want to do this for each breakdown_number. The most accurate method is probably to use variables:
INSERT INTO second_table(physical_account, logical_account)
SELECT MAX(CASE WHEN seqnum = 1 THEN t.physical_account END),
MAX(CASE WHEN seqnum = 2 THEN t.logical_account END)
FROM (SELECT t.*,
(#rn := if(#b = t.breakdown_number, #rn + 1,
if(#b := t.breakdown_number, 1, 1)
)
) as seqnum
FROM Temp t CROSS JOIN
(SELECT #rn := 0, #b := -1) params
ORDER BY t.breakdown_number, t.sequence_number
) t
WHERE rn IN (1, 2)
GROUP BY t.breakdown_number;
If the sequence_number restarts at 1 for each breakdown_number, then the subquery and variables are not needed:
INSERT INTO second_table(physical_account, logical_account)
SELECT MAX(CASE WHEN t.sequence_number = 1 THEN t.physical_account END),
MAX(CASE WHEN t.sequence_number = 2 THEN t.logical_account END)
FROM Temp t
WHERE t.sequence_number IN (1, 2)
GROUP BY t.breakdown_number;
Finally, in some cases, you can just use a hack:
INSERT INTO second_table(physical_account, logical_account)
SELECT SUBSTRING_INDEX(GROUP_CONCAT(t.physical_account), ',', 1),
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t.logical_account), ',', 2), ',', -1)
FROM Temp t
WHERE t.sequence_number IN (1, 2)
GROUP BY t.breakdown_number;
Notes about this approach:
It converts the accounts to strings, if they are of some other time.
group_concat() has a (configurable) maximum length, so if there are many records for a given breakdown_number, then you can get a run-time error.

You can use a sub query in the select with LIMIT OFFSET:
INSERT INTO second_table (physical_account, logical_account)
SELECT t.physical_account,
(SELECT s.logical_account FROM temp s
ORDER BY s.breakdown_number
LIMIT 1,1)
FROM Temp t
ORDER BY t.breakdown_number
LIMIT 1
This will select the first and second values based on breakdown_number on ACSENDING order.

DECLARE #physical_account varchar(30); /*Data Type as required*/
DECLARE #logical_account varchar(30);
SELECT #physical_account=physical_account FROM Temp WHERE logical_account=NULL AND physical_account='10001'
SELECT #logical_account=logical_account FROM Temp WHERE logical_account='0011' AND physical_account=NULL
INSERT INTO Table_New(physical_account, logical_account) VALUES(#physical_account, #logical_account);

Related

get the range of sequence values in table column

I have a list of value in my column. And want to query the range.
Eg. If values are 1,2,3,4,5,9,11,12,13,14,17,18,19
I want to display
1-5,9,11-14,17-19
Assuming that each value is stored on a separate row, you can use some gaps-and-island technique here:
select case when min(val) <> max(val)
then concat(min(val), '-', max(val))
else min(val)
end val_range
from (select val, row_number() over(order by val) rn from mytable) t
group by val - rn
order by min(val)
The idea is to build groups of consecutive values by taking the difference between the value and an incrementing rank, which is computed using row_number() (available in MySQL 8.0):
Demo on DB Fiddle:
| val_range |
| :-------- |
| 1-5 |
| 9 |
| 11-14 |
| 17-19 |
In earlier versions, you can emulate row_number() with a correlated subquery, or a user variable. The second option goes like:
select case when min(val) <> max(val)
then concat(min(val), '-', max(val))
else min(val)
end val_range
from (select #rn := 0) x
cross join (
select val, #rn := #rn + 1 rn
from (select val from mytable order by val) t
) t
group by val - rn
order by min(val)
As a complement to other answers:
select dn.val as dnval, min(up.val) as upval
from mytable up
join mytable dn
on dn.val <= up.val
where not exists (select 1 from mytable a where a.val = up.val + 1)
and not exists (select 1 from mytable b where b.val = dn.val - 1)
group by dn.val
order by dn.val;
1 5
9 9
11 14
17 19
Needless to say, but using an OLAP function like #GNB does, is orders of magnitude more efficient.
A short article on how to mimic OLAP functions in MySQL < 8 can be found at:
mysql-row_number
Fiddle
EDIT:
If another dimension is introduced (in this case p), something like:
select dn.p, dn.val as dnval, min(up.val) as upval
from mytable up
join mytable dn
on dn.val <= up.val
and dn.p = up.p
where not exists (select 1 from mytable a where a.val = up.val + 1 and a.p = up.p)
and not exists (select 1 from mytable b where b.val = dn.val - 1 and b.p = dn.p)
group by dn.p, dn.val
order by dn.p, dn.val;
can be used, see Fiddle2

MySQL behaviour when using ANY_VALUE multiple times

I want to get a random row for each group when using GROUP BY in MySQL 5.7. The most clean way to do it from my research is doing something like this:
SELECT ANY_VALUE(column_1), ANY_VALUE(column_2), ..., ANY_VALUE(column_n)
FROM table
GROUP BY column
Since there is no syntax for something like ANY_VALUE(*) or ANY_VALUE(column_1, column2, ..., column_n) I am left confused if with the above query each value can come from a different row, or if all ANY_VALUE fields will come from the same row.
If you want a random row, use row_number():
select t.*
from (select t.*,
row_number() over (partition by column order by rand()) as seqnum
from t
) t
where seqnum = 1;
I am guessing that this is also faster than group by, but you can check if that is the case.
In MySQL 5.7, you can use variables:
select t.*
from (select t.*,
(#rn := if(#c = column, #rn + 1,
if(#c := column, 1, 1)
)
) as rn
from (select t.* from t order by column, rand) t cross join
(select #c := '', #rn := 0) params
) t
where rn = 1;
Assuming the following schema and sample data:
create table tbl(
id int auto_increment primary key,
grp int not null,
val int not null,
index (grp)
);
insert into tbl (grp, val) values (1, 1);
insert into tbl (grp, val) values (1, 2);
insert into tbl (grp, val) values (1, 3);
insert into tbl (grp, val) values (2, 1);
insert into tbl (grp, val) values (2, 2);
Get distinct groups in a derived table (or use the base table for groups, if you have). Get a random primary key in a subquery in SELECT clause with ORDER BY rand() LIMIT 1. Then join the result as a derived table with the base table.
select t.*
from (
select (
select id
from tbl t
where t.grp = g.grp
order by rand()
limit 1
) as id
from (select distinct grp from tbl) g
) r
join tbl t using (id);
Result would be something like
| id | grp | val |
| --- | --- | --- |
| 2 | 1 | 2 |
| 4 | 2 | 1 |
View on DB Fiddle

SQL merge doublon ID row into unique row with more column

I have an headhache trying to get this simple request working in SQL on a very huge database, maybe some of you could help ?
ID|R1 |R2
1 | a | b
1 | c | d
2 | a | b
2 | c | d
I would like to make an sql select query to get instead :
ID|R1 |R2 |R3 |R4
1 | a | b | c | d
2 | a | b | c | d
Thank you for any help !
I'm going to offer a query which will have almost the same behavior as what you want, plus it will be fairly simple:
SELECT
ID, GROUP_CONCAT(val ORDER BY val) val
FROM
(
SELECT ID, R1 AS val FROM yourTable
UNION ALL
SELECT ID, R2 FROM yourTable
) t
GROUP BY ID;
Demo
This approach is desirable for several reasons. First, it is robust with regard to any arbitrary number of "columns" which a given ID might have. Second, it gives us the option to order each row of values any way we want. Finally, it will be much easier to maintain than an exact answer using session variables to simulate things like row number.
This is tricky, because you do not have enough ids in your table. One method is to use variables, add a sequence number, and aggregate:
select id,
max(case when rn = 1 then r1 end) as r1,
max(case when rn = 1 then r2 end) as r2,
max(case when rn = 2 then r1 end) as r3,
max(case when rn = 2 then r2 end) as r4
from (select t.*,
(#rn := if(#i = id, #rn + 1,
if(#i := id, 1, 1)
)
) as rn
from (select t.*
from t
order by t.id
) t cross join
(select #rn := 0, #i := -1) params
) t
group by id;
This answer is a little upgrade to #TimBiegeleisen answer.
if the table is big you also need to use
SET SESSION group_concat_max_len = ##max_allowed_packet;
This query wil convert the comma separted values from the GROUP_CONCAT function into columns by using nested SUBSTRING_INDEX functions.
Query
SELECT
ID
, SUBSTRING_INDEX(SUBSTRING_INDEX(val, ',', 1), ',', -1) AS r1
, SUBSTRING_INDEX(SUBSTRING_INDEX(val, ',', 2), ',', -1) AS r2
, SUBSTRING_INDEX(SUBSTRING_INDEX(val, ',', 3), ',', -1) AS r3
, SUBSTRING_INDEX(SUBSTRING_INDEX(val, ',', 4), ',', -1) AS r4
FROM (
SELECT
ID, GROUP_CONCAT(val ORDER BY val) val
FROM
(
SELECT ID, R1 AS val FROM yourTable
UNION ALL
SELECT ID, R2 FROM yourTable
) t
GROUP BY ID
) x
see demo http://rextester.com/SDF72100

Check if a user was "active" in multiple rows - MySQL

How would I go about creating group_ids in the following example based on the area(s) the users are active in?
group_id rep_id area datebegin dateend
1 1000 A 1/1/15 1/1/16
1 1000 B 1/1/15 1/1/16
2 1000 C 1/2/16 12/31/99
In the table you can see that rep 1000 was active in both A and B between 1/15 and 1/16. How would I go about coding the group_id field to group by datebegin & dateend?
Thanks for any help.
You can use variables in order to enumerate groups of records having identical rep_id, datebegin, dateend values:
SELECT rep_id, datebegin, dateend,
#rn := IF(#rep_id <> rep_id,
IF(#rep_id := rep_id, 1, 1),
#rn + 1) AS rn
FROM (
SELECT rep_id, datebegin, dateend
FROM mytable
GROUP BY rep_id, datebegin, dateend) AS t
CROSS JOIN (SELECT #rep_id := 0, #rn := 0) AS v
ORDER BY rep_id, datebegin
Output:
rep_id, datebegin, dateend, rn
-----------------------------------
1000, 2015-01-01, 2016-01-01, 1
1000, 2016-02-01, 2099-12-03, 2
You can use the above query as a derived table and join back to the original table. rn field is the group_id field you are looking for.
You can use variables to assign groups. As you said, only if the date_begin and date_end exactly match for 2 rows, they would be in the same group. Else a new group starts.
select rep_id,area,date_begin,date_end,
,case when #repid <> rep_id then #rn:=1 --reset the group to 1 when rep_id changes
when #repid=rep_id and #begin=date_begin and #end=date_end then #rn:=#rn --if rep_id,date_begin and date_end match use the same #rn previously assigned
else #rn:=#rn+1 --else increment #rn by 1
end as group_id
,#begin:=date_begin
,#end:=date_end
,#repid:=rep_id
from t
cross join (select #rn:=0,#begin:='',#end:='',#repid:=-1) r
order by rep_id,date_begin,date_end
The above query includes variables in the output. To only get the group_id use
select rep_id,area,date_begin,date_end,group_id
from (
select rep_id,area,date_begin,date_end
,case when #repid <> rep_id then #rn:=1
when #repid=rep_id and #begin=date_begin and #end=date_end then #rn:=#rn
else #rn:=#rn+1
end as group_id
,#begin:=date_begin
,#end:=date_end
,#repid:=rep_id
from t
cross join (select #rn:=0,#begin:='',#end:='',#repid:=-1) r
order by rep_id,date_begin,date_end
) x

With MySQL, how can I generate a column containing the record index in a table?

Is there any way I can get the actual row number from a query?
I want to be able to order a table called league_girl by a field called score; and return the username and the actual row position of that username.
I'm wanting to rank the users so i can tell where a particular user is, ie. Joe is position 100 out of 200, i.e.
User Score Row
Joe 100 1
Bob 50 2
Bill 10 3
I've seen a few solutions on here but I've tried most of them and none of them actually return the row number.
I have tried this:
SELECT position, username, score
FROM (SELECT #row := #row + 1 AS position, username, score
FROM league_girl GROUP BY username ORDER BY score DESC)
As derived
...but it doesn't seem to return the row position.
Any ideas?
You may want to try the following:
SELECT l.position,
l.username,
l.score,
#curRow := #curRow + 1 AS row_number
FROM league_girl l
JOIN (SELECT #curRow := 0) r;
The JOIN (SELECT #curRow := 0) part allows the variable initialization without requiring a separate SET command.
Test case:
CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);
Test query:
SELECT l.position,
l.username,
l.score,
#curRow := #curRow + 1 AS row_number
FROM league_girl l
JOIN (SELECT #curRow := 0) r
WHERE l.score > 50;
Result:
+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
| 3 | c | 75 | 1 |
| 5 | e | 55 | 2 |
| 6 | f | 80 | 3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)
SELECT #i:=#i+1 AS iterator, t.*
FROM tablename t,(SELECT #i:=0) foo
Here comes the structure of template I used:
select
/*this is a row number counter*/
( select #rownum := #rownum + 1 from ( select #rownum := 0 ) d2 )
as rownumber,
d3.*
from
( select d1.* from table_name d1 ) d3
And here is my working code:
select
( select #rownum := #rownum + 1 from ( select #rownum := 0 ) d2 )
as rownumber,
d3.*
from
( select year( d1.date ), month( d1.date ), count( d1.id )
from maindatabase d1
where ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
group by YEAR( d1.date ), MONTH( d1.date ) ) d3
You can also use
SELECT #curRow := ifnull(#curRow,0) + 1 Row, ...
to initialise the counter variable.
Assuming MySQL supports it, you can easily do this with a standard SQL subquery:
select
(count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
username,
score
from league_girl l2
order by score;
For large amounts of displayed results, this will be a bit slow and you will want to switch to a self join instead.
If you just want to know the position of one specific user after order by field score, you can simply select all row from your table where field score is higher than the current user score. And use row number returned + 1 to know which position of this current user.
Assuming that your table is league_girl and your primary field is id, you can use this:
SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>
I found the original answer incredibly helpful but I also wanted to grab a certain set of rows based on the row numbers I was inserting. As such, I wrapped the entire original answer in a subquery so that I could reference the row number I was inserting.
SELECT * FROM
(
SELECT *, #curRow := #curRow + 1 AS "row_number"
FROM db.tableName, (SELECT #curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;
Having a subquery in a subquery is not very efficient, so it would be worth testing whether you get a better result by having your SQL server handle this query, or fetching the entire table and having the application/web server manipulate the rows after the fact.
Personally my SQL server isn't overly busy, so having it handle the nested subqueries was preferable.
I know the OP is asking for a mysql answer but since I found the other answers not working for me,
Most of them fail with order by
Or they are simply very inefficient and make your query very slow for a fat table
So to save time for others like me, just index the row after retrieving them from database
example in PHP:
$users = UserRepository::loadAllUsersAndSortByScore();
foreach($users as $index=>&$user){
$user['rank'] = $index+1;
}
example in PHP using offset and limit for paging:
$limit = 20; //page size
$offset = 3; //page number
$users = UserRepository::loadAllUsersAndSortByScore();
foreach($users as $index=>&$user){
$user['rank'] = $index+1+($limit*($offset-1));
}