Найти значение, которое может быть 0 или может быть больше 0, но должно иметь хотя бы одну запись равную 0 - PullRequest
1 голос
/ 04 марта 2020

Мне нужно найти записи, которые равны 0 и имеют другие записи, которые больше 0. В результате должна быть как минимум одна запись, равная 0, а также должна быть хотя бы одна запись, которая больше 0. Это проще Объясните это визуально:

Name amount
a1     0 
a1    100
a1    200
a2     0 
a2     0 
a2    200
a3    200
a3     0 
a3    100

Это не должно выглядеть так:

Name amount
a5    100 
a5    100
a5    200
a7     0 
a7     0 
a7     0
a6    200
a6    10 
a6    100

Я пробовал это:

Select name, amount 
from table1 
where amount = '0' AND amount > '0'

Извините, если этот вопрос немного неоднозначно, это довольно сложно объяснить.

Заранее спасибо.

NB - Извините, если вопрос недостаточно ясен, не знаю, как его сформулировать.

Ответы [ 4 ]

2 голосов
/ 04 марта 2020

SELECT даст вам строки из таблицы, а WHERE применяется к этим строкам, чтобы отфильтровать их. Итак, ваш пример:

Select name, amount from table1 where amount = '0' AND amount > '0'

никогда не вернет никаких строк, потому что он возвращает только те строки, которые имеют количество = 0 и количество> 0, что невозможно. Также я надеюсь, что эти значения имеют числовые значения c, поэтому не следует использовать одинарные кавычки вокруг них (т. Е. '0' должно быть простым 0)

У GMB есть хороший способ сделать это с функциями секционирования. Подзапрос преобразует данные в новый набор результатов, который содержит новые столбцы min_amount и max_amount для всех строк с одинаковым идентификатором вместе с другими данными для каждой строки. Затем вы можете фильтровать эти значения, хотя вы не упоминаете, могут ли присутствовать отрицательные значения.

Еще один способ сделать это - добавить проверки к критериям фильтра:

select name, amount
from table1 a
where a.id in (select id from table1 where amount = 0)
  and a.id in (select id from table1 where amount > 0)

Выбирает строки, в которых id находится в списке идентификаторов с 0 в качестве суммы и в списке идентификаторов с суммами> 0.

1 голос
/ 04 марта 2020

Я бы сказал это следующим образом:

Select t1.name, t1.amount 
from table1 t1
where (t1.amount = 0 and
       exists (select 1 from table1 tt1 where tt1.name = t1.name and tt1.amount > 0
      ) or
      (t1.amount > 0 and
       exists (select 1 from table1 tt1 where tt1.name = t1.name and tt1.amount = 0
      ) ;

Это может использовать индекс на table1(name, amount). И прелесть дополнительного сравнения заключается в том, что для каждой строки в исходной таблице нужно оценивать только одно предложение exists.

1 голос
/ 04 марта 2020

И так как обычно есть несколько способов решить любую проблему, вот еще один.

У вас вопрос довольно нормальный. Выберите набор данных, который удовлетворяет условиям N . В этом случае их всего два, и оба требуют наличия определенного типа строки для заданных значений. Таким образом, другой способ получить это - использовать предложение EXISTS.

Это логически очень похоже на метод Джейсона, использующий предложения IN. Ответ Джейсона , кстати, заслуживает того, чтобы быть выбранным в качестве ответа. Я просто предлагаю это как альтернативный подход, чтобы помочь вам сохранить ваши параметры открытыми.

В предложении EXISTS используется «коррелированный подзапрос», что означает, что значение из external query, table1 as t1, используется внутренними запросами (внутри скобок, если это помогает вам представить его), обычно в предложении WHERE внутреннего запроса.

Итак, каждое из предложений EXISTS ищет любое вхождение условий в их предложениях WHERE; имена совпадают, а суммы соответствуют их критериям. Если найдено любое вхождение , запрос возвращает набор результатов, который EXISTS интерпретирует как ИСТИНА. Если нет, ничего не возвращается, что EXISTS интерпретирует как ЛОЖЬ. Обратите внимание, что результаты внутреннего запроса вообще не имеют значения, просто удовлетворяется предложение WHERE или нет. Поэтому я использую SELECT 1, чтобы показать, что возвращаемое значение не имеет значения. На самом деле вы можете положить туда все, что захотите; даже 1/0, и он будет работать просто отлично.

select
  *
from 
  table1 as t1
where 
  exists (select 1 
          from table1 as t2 
          where t2.amount = 0
          and t2.Name = t1.Name)
  and
  exists (select 1
          from table1 as t3
          where t3.amount > 0
          and t3.Name = t1.Name);

Смотрите его в действии здесь: Rextester demo

1 голос
/ 04 марта 2020

Вы можете использовать оконные функции. Предполагая, что нет отрицательных значений, вы можете сделать:

select name, amount
from (
    select
        t.*,
        min(amount) over(partition by name) min_amount,
        max(amount) over(partition by name) max_amount
    from mytable t
) t
where min_amount = 0 and max_amount > 0

Если есть отрицательные значения:

select name, amount
from (
    select
        t.*,
        max(case when amount = 0 then 1 end) over(partition by name) has_zero_amount,
        max(amount) over(partition by name) max_amount
    from mytable t
) t
where has_zero_amount = 1 and max_amount > 0

Я бы ожидал, что оконные функции будут более эффективными, чем другие решения, включая несколько подзапросов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...