Alternatywa dla klauzuli WHERE [zamknięte]

16

Czy istnieje sposób tylko na SELECTwiersze z określonymi danymi w kolumnie, bez użycia WHERE?

np. gdybym to miał:

SELECT * FROM Users
WHERE town = 'Townsville'

czy istnieje sposób na implementację WHEREklauzuli w SELECTinstrukcji?

coś jak

SELECT *, town('Townsville') FROM Users

To dziwne pytanie, ale zadają je moi rówieśnicy

Josh Stevenson
źródło
4
Zapytaj ich, dlaczego o to pytają. Kontekst jest ważny.
Aaron Bertrand
@AaronBertrand, MikaelEriksson - Zasadniczo jest to mały kwestionariusz dotyczący SQL w pracy, natknąłem się na pytanie: „Wybierz wszystkich użytkowników z tabeli użytkowników, którzy pochodzą z Townsville, bez użycia klauzuli where” I nie wiedziałem o tym było możliwe! Może podchodzę do tego niewłaściwie…?
Josh Stevenson
Oświadczam również, że ten kwestionariusz nie jest w żaden sposób powiązany z moim statusem pracownika w firmie! To tylko trochę zabawy
Josh Stevenson

Odpowiedzi:

17

Nie jestem pewien, czy tego rodzaju zwariowanych rzeczy szukałeś ...

Oświadczenie : Absolutnie nie mam pojęcia, dlaczego chcesz tego użyć.

SELECT * 
FROM Users AS u
INNER JOIN (SELECT 'Townsville' town) towns 
  ON towns.town = u.Town;
Mark Sinkinson
źródło
17

Dane

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');

Alternatywne rozwiązania

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

Zatrzymywanie duplikatów

-- :)
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;

Wynik:

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

Na przykład:

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

Wypróbuj tutaj: Stack Exchange Data Explorer

Paul White przywraca Monikę
źródło
Bardzo dobrze! Zgaduję, że nie byłbym w stanie użyć tego w scenariuszu, w którym nie mam kolumny zawierającej tylko unikalne dane, takie jak „Nazwa użytkownika”? Np. Gdybym tylko miał imię, nazwisko, miejscowość.
Josh Stevenson
3
@JoshStevenson Prawidłowo, chociaż dodałem odpowiednio szalony sposób, aby zachować duplikaty jako ostatni przykład, a potem rozsądny.
Paul White przywraca Monikę
1
W przypadku GROUP BYrozwiązań można również dodać PK do grupy według listy (aby być w 100% pewnym, że zapytania zwracają tę samą liczbę wierszy co GDZIE). Zakładając oczywiście, że jest PK;)
ypercubeᵀᴹ
14

„Po prostu dla zabawy” można użyć order byztop(1) with ties

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

Spowoduje to uporządkowanie wszystkich wierszy Townsvilleod pierwszego, ponieważ sprawa zwraca 1if town = 'Townsville'. Wszystkie pozostałe wiersze będą 2zwracane przez skrzynkę.

with tiesKlauzula sprawia, że zapytanie zwróci wszystkie wiersze, które jest „tie” na ostatnim miejscu w rzędach zwrócone. Użycie top(1)w połączeniu z with tieszwróci następnie wszystkie wiersze, które mają tę samą wartość, co pierwszy wiersz w wyrażeniu użyty w kolejności według klauzuli.

Uwaga: jak zauważył Martin Smith w komentarzu, zwróci wszystkie wiersze, jeśli poprosisz o miasto, którego nie ma w tabeli.

Jeśli nie boisz się XML-a baz danych, możesz skorzystać z predykatu w funkcji nodes ().

Pożyczenie zestawu od Paula White'a.

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);

Kolejna wersja z topi order byże rzeczywiście praca przy wyszukiwaniu nie istniejących miast.

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
Mikael Eriksson
źródło
7

Masz tutaj dwie różne rzeczy.

SELECT * FROM Users
WHERE town = 'Townsville'

Ograniczy liczbę wierszy, które otrzymasz z powrotem, tylko do tych, w których miasto =Townsville

SELECT *, town('Townsville') FROM Users

Prześle literał Townsvilledo funkcji o nazwie town. Nie ograniczy to wierszy zwracanych przez zapytanie, a jeśli funkcja zwróci cokolwiek poza jedną wartością, pojawi się błąd.

Istnieją inne sposoby ograniczenia liczby wierszy zwracanych z zapytania. Na przykład klauzula HAVING. Ale ma kilka innych wymagań.

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

Lub WEJŚCIE DO WEWNĘTRZNEJ, choć ten jest nieco dziwny, jeśli nie masz drugiego stołu.

SELECT * FROM Users
INNER JOIN (SELECT 1 col1) UselessTable
    ON Users.town = 'Townsville'
Kenneth Fisher
źródło
5

Oto przykład z wykorzystaniem wspólnego wyrażenia tabelowego (CTE).

with Town as 
(
    select 'Townsville' as Town
)
select *
  from Users u
  join Town  t on u.Town = t.Town
datagod
źródło
5

Możesz to zrobić:

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

Ściśle mówiąc, nie używasz klauzuli WHERE

druzin
źródło
5

Oto idiotyczny, całkowicie logiczny sposób na zrobienie tego, czego jeszcze nie widzę ...

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

Nie mogę sobie wyobrazić, dlaczego nie była to pierwsza sugestia. :)

Erik
źródło
-2
SELECT *, 
    case when town='Townsville' then 'Townsville' 
         else null 
    end as Town
FROM Users

Wszystkie miasta oprócz Townsville byłyby zerowe. Problem rozwiązany.

ColorfulWind
źródło