Альтернатива пункту WHERE [закрито]


16

Чи є спосіб в SELECTрядках лише рядків із певними даними, не використовуючи WHERE?

Наприклад, якби у мене це було:

SELECT * FROM Users
WHERE town = 'Townsville'

чи є спосіб реалізувати WHEREпункт до SELECTзаяви?

щось на зразок

SELECT *, town('Townsville') FROM Users

Це химерне питання, але це одне мене задали однолітки


4
Запитайте їх, чому вони це просять. Контекст важливий.
Аарон Бертран

@AaronBertrand, MikaelEriksson - В основному це невеликий опитувальник щодо SQL на роботі, я натрапив на запитання "Виберіть усіх користувачів із таблиці користувачів, які приїжджають з Таунсвілла, не використовуючи пункт" де ", і я не знав, що було можливо! Можливо, я підходжу до цього неправильним шляхом ..?
Джош Стівенсон

Також просто зазначив, що ця анкета жодним чином не пов'язана з моїм статусом працевлаштування в компанії! Це просто трохи весело
Джош Стівенсон

Відповіді:


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рішень ви також можете додати ПК у групу за списком (щоб бути на 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першими, оскільки справа повертається, 1якщо town = 'Townsville'. Усі інші рядки матимуть 2повернення справи.

with tiesПоложення робить запит повертає всі рядки, є «краваткою» на останнє місце в повертаються рядках. Використання top(1)в поєднанні з with ties, потім поверне всі рядки, які мають те саме значення, що і перший рядок у виразі, використаний у порядку за допомогою пункту.

Зауважте, як в коментарі зазначив Мартін Сміт, він поверне всі рядки, якщо ви попросите місто, якого немає в таблиці.

Якщо ви не боїтеся речей XML баз даних, ви можете використовувати предикат у функції nodes ().

Позиція налаштування від Пола Вайт.

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'

Обмежить кількість рядків, які ви отримаєте назад, лише до тих, де місто =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

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

Я не уявляю, чому це було не перше, що було запропоновано. :)


-2
SELECT *, 
    case when town='Townsville' then 'Townsville' 
         else null 
    end as Town
FROM Users

Усі міста, окрім Таунсвілла, були б недійсними. Проблема вирішена.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.