Как я могу написать запрос if else-if else в MySQL при возврате переменного числа строк - PullRequest
5 голосов
/ 07 октября 2011

У меня есть три столбца в таблице:

score  status  No.
1,     2,      1
0,     1,      2
0,     0,      1

Мне нужно это, чтобы написать псевдо SQL в стиле C #:

rows = empty;

rows = "SELECT * FROM `table` WHERE score = 1"
if (rows.Count > 0) //at least one row
    return rows;

rows = "SELECT * FROM `table` WHERE status = 2"
if (rows.Count > 1) //more than one row 
    return row with MAX(No.) from rows; //ie MAX(No.) where status = 2

 return rows;

Я надеюсь, что смогу быть ясным. Короче говоря, выберите из моей таблицы записи с счетом = 1, и, если такой записи нет, верните запись, где статус = 2, и если существует более одной записи с состоянием = 2, то верните запись с максимальным значением. для №, где статус = 2 (если вообще нет записи со статусом = 2, вернуть пусто).

Как мне написать это в одном запросе? Это должно быть хорошим опытом обучения для меня. В противном случае я знаю, чтобы разбить на более мелкие запросы и запустить каждый. И я не могу сейчас пойти с хранимыми процедурами ..

Редактировать: На самом деле в моем запросе будет еще несколько предложений WHERE, но они идентичны в обоих условиях, поэтому я и опустил его. Следовательно, что касается первого условия, на данный момент будет возвращена только одна запись. То есть SELECT * FROM table WHERE score = 1 покажет только одну строку. И мне нужно / я приму ответы , что тоже дает такое решение. Но дело в том, что вы никогда не знаете, может быть, в будущем с некоторыми изменениями дизайна, может быть больше строк с score = 1. Вот почему я пошел на записей вместо записи . Но в идеале бизнес-логика должна иметь все записи с score = 1. На данный момент сделаем запись. Я просто думаю, что запрос будет намного проще, если будет возвращена только одна строка, и мои товарищи по команде смогут легко усвоить код.

Окончательное обновление : Спасибо всем, ребята, вы были очень любезны :) Многие ответы сработали хорошо, и выбор одного действительно ужасен. Моя находка по ответам:

  1. Ответы, которые работали всегда: @ GordonLinoff's , @ ZaneBiens's , @ еще один ZaneBien , @ Scen's , @JulienCh .'s (последние 3 по существу одинаковы, но я пока не до конца осознаю, как они работали:))

  2. Ответы, которые работали только тогда, когда первое условие score = 1 вернуло только одну строку: @ HannoBinder's , @ ShlomiNoach's , @ DaniellePaquette-Harvey's . На данный момент, я буду придерживаться @ Danielle's (что чертовски просто) и позже вернусь, если возникнет необходимость иметь более одной строки)

  3. Остальные ответы я не смог проверить, поскольку они либо не очень специфичны, либо не связаны с MySQL.

  4. @ MatthewPK не подходит в контексте.

Присуждение награды и принятие ответа сложно с таким количеством правильных ответов. Я выбрал этот , так как чувствовал, что он, вероятно, более эффективен и удобен для чтения, но я приму ответ Scen's за то, что он совершенно простой.

Ответы [ 12 ]

5 голосов
/ 10 июля 2012

Это кажется трудным, поскольку вы возвращаете переменное число строк в двух случаях.

Следующий запрос объединяет все возможные строки, а затем проверяет наличие первого типа, чтобы увидеть, какой тип должендолжны быть возвращены.

select *
from ((SELECT 'SCORE' as matchtype, t.*
       FROM `table` t
       WHERE score = 1
      ) union all
      (SELECT 'STATUS' as matchtype, t.*
       FROM `table` t join
            (select max(`No.`) as maxno
             from `table` 
             WHERE status = 2
           ) tsum
           on t.`No.` = tsum.maxno
       WHERE status = 2
      )
     ) t cross join
     (select count(*) as cnt
      from `table`
      where score = 1
     ) const
where matchtype = (case when const.cnt > 0 then 'SCORE' else 'STATUS' end)

Примечание: возвращаемый набор включает в себя MatchType.Чтобы избавиться от этого, вам нужно будет включить полный список нужных столбцов.

4 голосов
/ 10 июля 2012

Этот запрос решит ваш вопрос:

SELECT *
  FROM `your_table`
WHERE
  status = 2 OR score = 1 
ORDER BY
  if(score = 1, 1, 0) DESC, 
  if(status = 2, 1, 0) DESC,
  `No.` DESC
LIMIT 1;

В отличие от других ответов:

  • Не принимает положительные значения для столбца No.
  • Это синтаксис MySQL (вы пометили mysql)

Но не стесняйтесь отдать должное / репутацию KSiimson, который проложил правильный путь. К сожалению, в его запросе есть две ошибки.

Также обратите внимание, что указанный запрос не может использовать какой-либо индекс в предложении ORDER BY, поскольку мы обертываем столбцы функциями. Индексы вряд ли будут использоваться в FROM clause из-за условия OR.

3 голосов
/ 12 июля 2012

Вот гораздо более эффективное решение, которое должно работать для вас:

SELECT * FROM tbl WHERE score = 1 

UNION ALL

(
    SELECT a.* 
    FROM tbl a
    INNER JOIN
    (
        SELECT COUNT(1) AS s1exists FROM tbl WHERE score = 1
    ) b ON b.s1exists = 0
    WHERE a.status = 2
    ORDER BY a.no DESC
    LIMIT 1
)

Разбивка:

Как работает этот запрос, так это то, что первый SELECT в UNION возвращает все строки, где score = 1. Второй SELECT возвращает максимальную строку (на основе поля no), где status = 2 И , где нет ни одной строки, где score = 1. Если есть только одна строка или несколько строк, где status = 2, максимальная строка одинакова для обоих .

Это удовлетворяет всем вашим условиям, которые вы указали:


  • Одна или несколько строк существуют, где score = 1 : First SELECT возвращает все эти строки; секунда SELECT возвращает пустое значение (даже если есть строки с status = 2).

  • Нет строк, где score = 1, и есть одна строка, где status = 2 : Первый SELECT возвращает пустой; секунда SELECT возвращает одну строку (то есть то же самое , что и максимальная строка).

  • Нет строк, где score = 1, и существует более одной строки, где status = 2 : Первый SELECT возвращает пустой; секунда SELECT возвращает максимальную строку, где status = 2.

  • Строков вообще не существует, где либо score = 1, либо status = 2 : Первая строка ничего не возвращает; вторая строка также ничего не возвращает, отображая пустой результат.
2 голосов
/ 10 июля 2012

Полностью не проверено, скорее всего, очень неэффективно, но может работать:

SELECT * FROM `table`
WHERE score=1

union all

SELECT * FROM `table`
WHERE status=2 
and NOT EXISTS (SELECT * FROM `table` WHERE score=1)
and `No.` = (
             SELECT max(`No.`) FROM `table`
             WHERE status=2 
            )
1 голос
/ 17 июля 2012
Select *
From scores
Where score = 1
UNION
(
   Select *
   From scores
   Where status = 2 and not exists (Select * from scores where score = 1)
   Order By `No.` desc
   Limit 1
);

Я назвал table как scores.

SqlFiddle (Запрос с какой-то поддельной схемой, чтобы вы могли увидеть результаты): http://sqlfiddle.com/#!2/6aac2/1/0

РЕДАКТИРОВАТЬ: отредактировал SQlFiddle, чтобы сопоставить поддельную схему с той, что в вопросе.

1 голос
/ 16 июля 2012

Вот мое другое решение , но я публикую это как другой ответ, потому что он использует совершенно другую технику (условные условия в предложении WHERE) вместо использования UNION.

Это исключает необходимость использования UNION операторов, что, возможно, является тем, что вам нужно (даже если технически использовать UNION из двух операторов считается как «один» запрос). Другое преимущество заключается в том, что она независима от СУБД и не использует ORDER BY X / LIMIT 1, которая в основном основана на MySQL, для получения минимальных и максимальных строк:

SELECT a.*
FROM tbl a
CROSS JOIN (SELECT COUNT(1) AS score1cnt FROM tbl WHERE score = 1) b
CROSS JOIN (SELECT MAX(no) AS maxno FROM tbl WHERE status = 2) c
WHERE
    CASE WHEN b.score1cnt > 0 THEN a.score = 1
         WHEN b.score1cnt = 0 AND c.maxno IS NOT NULL THEN a.status = 2 AND a.no = c.maxno
         ELSE 0
    END

Вы можете возиться с Демо SQLFiddle , которое включает оба моих ответа.

1 голос
/ 16 июля 2012
IF EXISTS(Select * From `table` Where score = 1)
Begin
    Select * From `table` Where score = 1;
End
Else
Begin
    Select *
    From `table`
    Where status = 2
    Order by `No.` desc
    LIMIT 1
End
1 голос
/ 16 июля 2012

Я не могу (пока?) Увидеть элегантное решение вашей проблемы только в одном запросе.

Кроме того, я не уверен, какова ваша настоящая проблема; Ваш пример кода ясен, но мне трудно экстраполировать проблему, которая будет соответствовать вашим требованиям, начиная с

мой запрос будет содержать еще несколько предложений WHERE, но в обоих условия

Кроме того, я удивлен, что до сих пор никто не предлагал использовать предложение group by. Не вдаваясь в синтаксические подробности MySQL здесь, но в основном

select score, status, MAX(No) as No
from tbl 
group by score, status

даст вам ровно одну запись на «группу» с максимальным значением для No.

Поместить в подзапрос вышеупомянутое select может быть уточнено, например,

select *
from ( [*] ) as tbl
where
   (score = 1)
OR (score != 1 AND status = 2)
OR /* ... more conditions of the above type... */
order by /* ... */
limit 1

([*] обозначает здесь оператор group ed select сверху)

Добавьте или удалите (вариант) предложения order by score DESC, status DESC и / или limit 1 по мере необходимости, и вы должны быть очень близки к тому, что вам нужно.

1 голос
/ 16 июля 2012

Я оставил свой оригинальный ответ, но он не так эффективен, как приведенный ниже:

/*
Query returns rows with score=1 when they exist or nothing
*/
select score, status, no from temp where score=1

UNION

/*
Query returns rows with status=2 when they exist and when there are no rows
 with score=1 or nothing otherwise.
*/
(

select t1.score, t1.status, t1.no from

( /* <subquery> */

(
select score, status, no from temp
where status=2 and (select max(score) from temp where score=1) is null
) as t1

join

(
select max(no) as maxNo from temp where status=2
) as t2

on t1.no=t2.maxNo

) /* </subquery> */

limit 1

)

Вы можете использовать индексы, чтобы сделать его более эффективным. Кроме того, я не уверен, что запрос в условном операторе (который проверяет, есть ли строки со счетом = 1) выполняется один или несколько раз. Чтобы быть в безопасности, вы можете заменить его на @c и добавить строку:

set @c:=(select max(score) from temp where score=1);

Оригинальный ответ:

/*
Query returns rows with score=1 when they exist or nothing
*/
select score, status, no from temp where score=1

UNION

/*
Query returns nothing when there are rows with score=1,
 rows with status=2 when they exist and when there are no rows with score=1, or
 nothing otherwise.

Does this by adding weights to rows.
Rows with score=1 have greater weights than rows with status=2.
Joins rows with greatest weights to rows with status=2.
*/
( /* <query> */

select t.score, t.status, t.no from
( /* <joins> */

/* Gets greatest weight */
(
select max(if(score=1, 2, if(status=2, 1, -1))) as w
from temp
) as t1

join

/*  Joined on rows with status=2 using assigned weights */
(
select score, status, no, if(score=1, 2, if(status=2, 1, -2)) as w
from temp
where status=2
) as t

on t.w=t1.w

join

/*  Joined on rows with greatest No. for rows with status=2 */
(
select max(no) as maxNo
from temp
where status=2
) as t2

on t.no=t2.maxNo

) limit 1 /* </joins> (Only returns one row with max No.) */

) /* </query> */
1 голос
/ 12 июля 2012

Вы можете получить первый результат из этого запроса:

-- If there is no row with Score 1, then the first SELECT will return nothing.
-- If there is a row with Score 1, then you will get it.
-- No row with Score 1. If there is one row with Status 2, then you will get it.
-- No row with Score 1. If there is more than one row with Status 2, then you will get the one with the highest No.

SELECT 1 AS ColumnOrder, Table.*
FROM   Table
WHERE  Score = 1
LIMIT  1
UNION ALL
SELECT   2 AS ColumnOrder, Table.* 
FROM     Table 
WHERE    Status=2 
ORDER BY ColumnOrder, No DESC
LIMIT    1 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...