Alternativa alla clausola WHERE [chiusa]


16

Esiste un modo per solo le SELECTrighe con determinati dati in una colonna, senza utilizzare WHERE?

ad es. se avessi questo:

SELECT * FROM Users
WHERE town = 'Townsville'

c'è un modo per implementare la WHEREclausola SELECTnell'istruzione?

qualcosa di simile a

SELECT *, town('Townsville') FROM Users

È una domanda bizzarra ma è una domanda che mi è stata posta dai miei colleghi


4
Chiedi loro perché lo stanno chiedendo. Il contesto è importante.
Aaron Bertrand

@AaronBertrand, MikaelEriksson - Fondamentalmente è un piccolo questionario su SQL al lavoro, ho trovato una domanda che afferma "Seleziona tutti gli utenti dalla tabella degli utenti che provengono da Townsville, senza usare una clausola where" E, non lo sapevo era possibile! Forse mi sto avvicinando a questo nel modo sbagliato ..?
Josh Stevenson,

Anche solo affermando che questo questionario non è in alcun modo collegato al mio stato di impiego presso l'azienda! È solo un po 'divertente
Josh Stevenson,

Risposte:


17

Non sono sicuro se questo è il tipo di cosa folle che stavi cercando ...

Disclaimer : non ho assolutamente idea del perché tu voglia usarlo.

SELECT * 
FROM Users AS u
INNER JOIN (SELECT 'Townsville' town) towns 
  ON towns.town = u.Town;

17

Dati

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

Soluzioni alternative

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

Conservare i duplicati

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

Produzione:

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

Per l'esempio finale:

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

Provalo qui: Stack Exchange Data Explorer


Molto bella! Suppongo che non sarei in grado di utilizzarlo in uno scenario in cui non ho una colonna contenente solo dati univoci come "Nome utente"? Ad esempio se avessi solo nome, cognome, città.
Josh Stevenson,

3
@JoshStevenson Corretto, anche se ho aggiunto un modo adeguatamente pazzo per preservare i duplicati come esempio finale, e quindi ragionevole.
Paul White Ripristina Monica

1
Per le GROUP BYsoluzioni, puoi anche aggiungere il PK nel gruppo per elenco (per essere sicuro al 100% che le query restituiscano lo stesso numero di righe di WHERE). Supponendo ovviamente che esista un PK;)
ypercubeᵀᴹ


14

"Solo per divertimento" è possibile utilizzare un order bycontop(1) with ties

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

Questo ordinerà prima tutte le righe Townsvillepoiché il caso restituisce 1if town = 'Townsville'. Tutte le altre righe verranno 2restituite dal caso.

Il with ties clausola fa sì che la query restituisca tutte le righe che sono un "pareggio" per l'ultimo posto nelle righe restituite. L'uso top(1)in combinazione con with tiesrestituirà quindi tutte le righe con lo stesso valore della prima riga nell'espressione utilizzata nella clausola order by.

Nota, come ha sottolineato Martin Smith in un commento, tornerà tutte le righe se chiedi una città che non esiste nella tabella.

Se non temi le cose XML dei database, puoi utilizzare un predicato nella funzione nodes ().

Prendendo in prestito l'installazione da Paul White.

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

Un'altra versione con tope order byche funziona effettivamente quando si cercano città non esistenti.

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

Hai due cose diverse qui.

SELECT * FROM Users
WHERE town = 'Townsville'

Limita il numero di righe che ritorni a quelle in cui town =Townsville

SELECT *, town('Townsville') FROM Users

Passerà il letterale Townsville a una funzione chiamata town. Non limiterà le righe restituite dalla query e infatti se la funzione restituisce qualcosa tranne un singolo valore, si otterrà un errore.

Esistono altri modi per limitare il numero di righe restituite da una query. La clausola HAVING per esempio. Ma ha molti altri requisiti.

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

O un INNER JOIN anche se questo è un po 'strano se non hai una seconda tabella.

SELECT * FROM Users
INNER JOIN (SELECT 1 col1) UselessTable
    ON Users.town = 'Townsville'

5

Ecco un esempio che utilizza un'espressione di tabella comune (CTE).

with Town as 
(
    select 'Townsville' as Town
)
select *
  from Users u
  join Town  t on u.Town = t.Town

5

Bene, potresti farlo:

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

A rigor di termini non stai usando la clausola WHERE


5

Ecco un modo completamente logico e idiota per farlo che non vedo ancora ...

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

Non riesco a immaginare perché questa non sia stata la prima cosa da suggerire. :)


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

Tutte le città oltre a Townsville sarebbero nulle. Problema risolto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.