Alternativa à cláusula WHERE [fechada]

16

Existe uma maneira de apenas SELECTlinhas com determinados dados em uma coluna, sem usar WHERE?

por exemplo, se eu tivesse isso:

SELECT * FROM Users
WHERE town = 'Townsville'

existe uma maneira de implementar a WHEREcláusula na SELECTdeclaração?

algo como

SELECT *, town('Townsville') FROM Users

É uma pergunta bizarra, mas é uma pergunta que meus colegas me perguntaram

Josh Stevenson
fonte
4
Pergunte a eles por que eles estão perguntando isso. O contexto é importante.
Aaron Bertrand
@AaronBertrand, MikaelEriksson - Basicamente, é um pequeno questionário sobre SQL no trabalho, me deparei com uma pergunta dizendo "Selecione todos os usuários da tabela de usuários que vêm de Townsville, sem usar uma cláusula where" E eu não sabia disso foi possível! Talvez eu esteja abordando isso da maneira errada ..?
21715 Josh Stevenson
Apenas afirmando que este questionário não está de forma alguma vinculado ao meu status de emprego na empresa! É só um pouco de diversão
Josh Stevenson

Respostas:

17

Não tenho certeza se esse é o tipo de coisa louca que você estava procurando ....

Disclaimer : Eu não tenho absolutamente nenhuma idéia de por que você iria querer usar isso.

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

Dados

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

Soluções alternativas

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

Reter duplicados

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

Resultado:

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

Para o exemplo final:

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

Experimente aqui: Stack Exchange Data Explorer

Paul White Restabelecer Monica
fonte
Muito agradável! Suponho que não seria capaz de usar isso em um cenário em que não tenho uma coluna contendo apenas dados exclusivos, como 'Nome de usuário'? Por exemplo, se eu tivesse apenas nome, sobrenome, cidade.
Josh Stevenson
3
@ JoshStevenson Correto, embora eu tenha adicionado uma maneira louca de preservar duplicatas como exemplo final, e depois sensato.
Paul White Restabelecer Monica
11
Para as GROUP BYsoluções, você também pode adicionar a PK no grupo por lista (para ter 100% de certeza de que as consultas retornam o mesmo número de linhas que o WHERE). Supondo, é claro, que exista um PK;)
ypercubeᵀᴹ
14

"Just for fun" você pode usar um order bycomtop(1) with ties

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

Isso ordenará todas as linhas com a Townsvilleprimeira, pois o caso retorna1 se town = 'Townsville'. Todas as outras linhas terão um 2retorno pelo caso.

o with ties cláusula faz com que a consulta retorne todas as linhas que estão "empatadas" para o último lugar nas linhas retornadas. Usar top(1)em combinação com with tiesretornará todas as linhas que tenham o mesmo valor que a primeira linha na expressão usada na cláusula order by.

Observe que, como Martin Smith apontou em um comentário, ele retornará todas as linhas se você solicitar uma cidade que não exista na tabela.

Se você não tem medo das coisas XML dos bancos de dados, pode usar um predicado na função nodes ().

Emprestando a configuração de 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);

Outra versão com tope order byque realmente funciona ao procurar cidades não existentes.

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
fonte
7

Você tem duas coisas diferentes aqui.

SELECT * FROM Users
WHERE town = 'Townsville'

Restringirá o número de linhas retornadas apenas para aquelas em que cidade =Townsville

SELECT *, town('Townsville') FROM Users

Vai passar o literal Townsvillepara uma função chamadatown . Ele não restringirá as linhas retornadas pela consulta e, de fato, se a função retornar algo além de um único valor, você receberá um erro.

Existem outras maneiras de restringir o número de linhas que você recebe de uma consulta. A cláusula HAVING, por exemplo. Mas tem vários outros requisitos.

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

Ou um INNER JOIN, embora este seja um pouco estranho se você não tiver uma segunda tabela.

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

Aqui está um exemplo usando uma expressão de tabela comum (CTE).

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

Bem, você poderia fazer isso:

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

A rigor, você não está usando a cláusula WHERE

druzin
fonte
5

Aqui está uma maneira completamente idiota de fazer isso que eu ainda não vejo ...

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

Não consigo imaginar por que isso não foi a primeira coisa a ser sugerida. :)

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

Todas as cidades além de Townsville seriam nulas. Problema resolvido.

Colorido
fonte