Альтернатива предложению WHERE [закрыто]

16

Есть ли способ только SELECTстроки с определенными данными в столбце, без использования WHERE?

например, если бы я имел это:

SELECT * FROM Users
WHERE town = 'Townsville'

Есть ли способ реализовать WHEREпредложение в SELECTзаявлении?

что-то вроде

SELECT *, town('Townsville') FROM Users

Это странный вопрос, но его задали мои коллеги.

Джош Стивенсон
источник
4
Спросите их, почему они спрашивают это. Контекст важен.
Аарон Бертран
@AaronBertrand, MikaelEriksson - По сути, это небольшая анкета о работе SQL, я натолкнулся на вопрос «Выберите всех пользователей из таблицы пользователей из Таунсвилла, не используя предложение where». И я не знал, что было возможно! Может быть, я подхожу к этому неправильный путь ..?
Джош Стивенсон
Также просто заявив, что эта анкета никак не связана с моим статусом сотрудника в компании! Это просто забавно
Джош Стивенсон

Ответы:

17

Не уверен, что это та сумасшедшая вещь, которую вы искали ....

Отказ от ответственности : я понятия не имею, почему вы хотели бы использовать это.

SELECT * 
FROM Users AS u
INNER JOIN (SELECT 'Townsville' town) towns 
  ON towns.town = u.Town;
Марк Синкинсон
источник
17

Данные

DECLARE @Example AS table
(
    UserName varchar(30) NULL,
    Town varchar(30) NULL
);

INSERT @Example
    (UserName, Town)
VALUES
    ('Aaron', 'Not Townsville'),
    ('Bob', 'Not Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Charles', 'Townsville'),
    ('Dan', 'Townsville'),
    ('Eric', 'Not Townsville');

Альтернативные решения

SELECT E.UserName, E.Town
FROM @Example AS E
GROUP BY E.Town, E.UserName
HAVING E.Town = 'Townsville'

-- OR

SELECT E.UserName, 'Townsville' AS Town
FROM @Example AS E
GROUP BY E.UserName
HAVING 1 = MAX(CASE WHEN E.Town = 'Townsville' THEN 1 ELSE 0 END);

-- OR

SELECT E.UserName, E.Town
FROM @Example AS E
INTERSECT
SELECT E.UserName, 'Townsville' AS Town
FROM @Example AS E

Сохранение дубликатов

-- :)
SELECT E.UserName, E.Town
FROM @Example AS E
CROSS APPLY (VALUES(NEWID())) AS CA (n)
GROUP BY E.Town, E.UserName, CA.n
HAVING E.Town = 'Townsville'

-- Simulating INTERSECT ALL
SELECT
    R.UserName,
    R.Town
FROM 
(
    SELECT 
        E.UserName, 
        E.Town, 
        rn =
            ROW_NUMBER() OVER (
                PARTITION BY E.UserName, E.Town 
                ORDER BY E.UserName, E.Town)
    FROM @Example AS E
    INTERSECT
    SELECT 
        E.UserName, 
        'Townsville', 
        rn = 
        ROW_NUMBER() OVER (
            PARTITION BY E.UserName 
            ORDER BY E.UserName)
    FROM @Example AS E
) AS R;

Выход:

╔══════════╦════════════╗
 UserName     Town    
╠══════════╬════════════╣
 Charles   Townsville 
 Dan       Townsville 
╚══════════╩════════════╝

Для последнего примера:

╔══════════╦════════════╗
 UserName     Town    
╠══════════╬════════════╣
 Charles   Townsville 
 Charles   Townsville 
 Charles   Townsville 
 Charles   Townsville 
 Dan       Townsville 
╚══════════╩════════════╝

Попробуйте это здесь: Stack Exchange Data Explorer

Пол Уайт восстановил Монику
источник
Очень хорошо! Я предполагаю, что не смогу использовать это в сценарии, где у меня нет столбца, содержащего только уникальные данные, такие как «Имя пользователя»? Например, если бы у меня было только имя, фамилия, город.
Джош Стивенсон
3
@JoshStevenson Правильно, хотя я добавил достаточно безумный способ сохранить дубликаты в качестве последнего примера, а затем разумный.
Пол Уайт восстановил Монику
1
Для GROUP BYрешений вы также можете добавить PK в список по группам (чтобы быть на 100% уверенным, что запросы возвращают то же количество строк, что и WHERE). Предполагая, конечно, что есть ПК;)
ypercubeᵀᴹ
14

«Просто для удовольствия» вы можете использовать order byсtop(1) with ties

select top(1) with ties *
from dbo.Users
order by case when town = 'Townsville' then 1 else 2 end;

Это упорядочит все строки Townsvilleпервым, так как регистр возвращает 1if town = 'Townsville'. Все остальные строки будут 2возвращены регистром.

Предложение with tiesзаставляет запрос возвращать все строки, которые являются «связующими» для последнего места в возвращаемых строках. Использование top(1)в сочетании с with tiesвернет все строки, которые имеют то же значение, что и первая строка в выражении, используемом в предложении order by.

Обратите внимание, как указал Мартин Смит в комментарии, он вернет все строки, если вы попросите указать город, которого нет в таблице.

Если вы не боитесь XML-данных баз данных, вы можете использовать предикат в функции node ().

Заимствование установки у Пола Уайта.

select T.X.value('(UserName/text())[1]', 'varchar(30)') as UserName,
       T.X.value('(Town/text())[1]', 'varchar(30)') as Town
from (
     select *
     from @Example
     for xml path('row'), type 
     ) as C(X)
  cross apply C.X.nodes('/row[Town = "Townsville"]') as T(X);

Еще одна версия с которой topи на order byсамом деле работает при поиске несуществующих городов.

select top(
          select sum(case when town = 'Townsville' then 1 end)
          from @Example
          ) *
from @Example
order by case when town = 'Townsville' then 1 else 2 end
Микаэль Эрикссон
источник
7

У вас есть две разные вещи здесь.

SELECT * FROM Users
WHERE town = 'Townsville'

Ограничит количество возвращаемых строк только теми, где town =Townsville

SELECT *, town('Townsville') FROM Users

Собирается передать литерал Townsvilleв функцию с именем town. Он не будет ограничивать строки, возвращаемые запросом, и фактически, если функция возвращает что-либо, кроме одного значения, вы получите ошибку.

Есть и другие способы ограничить количество строк, которые вы получаете от запроса. Предложение HAVING, например. Но у него есть несколько других требований.

SELECT town FROM Users
GROUP BY town
HAVING town = 'Townsville'

Или ВНУТРЕННЕЕ СОЕДИНЕНИЕ, хотя это немного странно, если у вас нет второй таблицы.

SELECT * FROM Users
INNER JOIN (SELECT 1 col1) UselessTable
    ON Users.town = 'Townsville'
Кеннет Фишер
источник
5

Вот пример использования общего табличного выражения (CTE).

with Town as 
(
    select 'Townsville' as Town
)
select *
  from Users u
  join Town  t on u.Town = t.Town
datagod
источник
5

Ну, вы могли бы сделать это:

    SELECT A.* 
    FROM Users A
         INNER JOIN Users B ON A.Id = B.Id AND B.town = 'Townsville'

Строго говоря, вы не используете предложение WHERE

Друзин
источник
5

Вот идиотский, совершенно логичный способ сделать это, которого я пока не вижу ...

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  -- Our important work should be all the database cares about
GO
BEGIN TRANSACTION

DECLARE @MyTableVar table(<all columns in order from the user table>, oldtown VARCHAR(50));

UPDATE users
SET town = N'Townsville'
OUTPUT 
     inserted.*  -- We don't want to have to type out the columns because that would be too much work
    deleted.town
INTO @MyTableVar;

--Display the result set of the table variable to prevent undesirables from sullying our output by inserting incorrect data even though we should have exclusive access.
SELECT * -- Select everything we want except for the 'oldtown' column because that data was probably wrong anyway
FROM @MyTableVar;

UPDATE u -- We don't want to be bad stewards of our data
SET
    town = oldtown
FROM users u
    INNER JOIN @MyTableVar mtv ON mtv.town = u.town, <Match up all the columns to REALLY ensure we are matching the proper row>

COMMIT TRANSACTION -- Make sure we save our work

Я не могу себе представить, почему это не было первым предложенным предложением. :)

Erik
источник
-2
SELECT *, 
    case when town='Townsville' then 'Townsville' 
         else null 
    end as Town
FROM Users

Все города, кроме Таунсвилла, будут нулевыми. Проблема решена.

ColorfulWind
источник