简体   繁体   中英

Select every other row as male/female from mysql table

I've got a table containing persons gender-coded as 0 and 1. I need to select every other row as male/female. I thought I could manage this somehow by using modulo and the gender-codes 0 and 1, but I haven't managed to figure it out yet...

The result I'm looking for would look like this:

+-----+--------+-------+
| row | gender | name  |
+-----+--------+-------+
|   1 | female | Lisa  |
|   2 | male   | Greg  |
|   3 | female | Mary  |
|   4 | male   | John  |
|   5 | female | Jenny |
+-----+--------+-------+

etc.

The alternative is to do it in PHP by merging 2 separate arrays, but I would really like it as a SQL query...

Any suggestions are appreciated!

Do two subqueries to select male and female. Use ranking function to have them enumerated.

Males:
1 | Peter
2 | John
3 | Chris

Females:
1 | Marry
2 | Christina
3 | Kate

Then multiplay ranking result by x10 and add 5 for females. So you have this:

Males:
10 | Peter
20 | John
30 | Chris

Females:
15 | Marry
25 | Christina
35 | Kate

Then do the UNION ALL and sort by new sort order/new ID.

Together it should like this (pseudo code)

SELECT
Name
FROM
(subquery for Males: RANK() AS sortOrd, Name)
UNION ALL
(subquery for Females: RANK()+1 AS SortOrd, Name)
ORDER BY SortOrd

Result should be like this:

Males and Females:
10 | Peter
15 | Marry
20 | John
25 | Christina
30 | Chris
35 | Kate  

Found Emulate Row_Number() and modified a bit for your case.

set @rownum := 0;
set @pg := -1;

select p.name, 
       p.gender
from
  (
    select name,
           gender,
           @rownum := if(@pg = gender, @rownum+1, 1) as rn, 
           @pg := gender as pg 
    from persons
    order by gender
  ) as p
order by p.rn, p.gender

Try on SQL Fiddle

Note: From 9.4. User-Defined Variables

As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed.

I will leave it up to you do decide if you can use this. I don't use MySQL so I can't really tell you if you should be concerned or not.

Similar to Mikael's solution but without the need to order the resultset multiple times -

SELECT *
FROM (
    SELECT people.*,
        IF(gender=0, @mr:=@mr+1, @fr:=@fr+1) AS rank
    FROM people, (SELECT @mr:=0, @fr:=0) initvars
) tmp
ORDER BY rank ASC, gender ASC;

To avoid having to order both the inner and outer selects I have used separate counters (@mr - male rank, @fr - female rank) in the inner select.

I've got a table containing persons gender-coded as 0 and 1

Then why would you make assumptions on the order of rows in the result set? Seems to me transforming the 0/1 into 'male'/'female' is far more robust:

select name, case gender when 0 then 'male' else 'female' end
from Person
SELECT alias.*, ROW_NUMBER() OVER (PARTITION BY GENDER ORDER BY GENDER) rnk
FROM TABLE_NAME
ORDER BY rnk, GENDER DESC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM