Alternativa a la cláusula WHERE [cerrada]


16

¿Hay alguna manera de solo SELECTfilas con ciertos datos en una columna, sin usar WHERE?

Por ejemplo, si tuviera esto:

SELECT * FROM Users
WHERE town = 'Townsville'

¿Hay alguna manera de implementar la WHEREcláusula en la SELECTdeclaración?

algo como

SELECT *, town('Townsville') FROM Users

Es una pregunta extraña pero es una pregunta que me han hecho mis compañeros.


44
Pregúnteles por qué preguntan esto. El contexto es importante.
Aaron Bertrand

@AaronBertrand, MikaelEriksson - Básicamente es un pequeño cuestionario sobre SQL en el trabajo, me encontré con una pregunta que decía "Seleccione todos los usuarios de la tabla de usuarios que provienen de Townsville, sin usar una cláusula where" Y, no sabía eso fue posible! ¿Tal vez me estoy acercando a esto de la manera incorrecta ...?
Josh Stevenson

¡También declaro que este cuestionario no está relacionado de ninguna manera con el estado de mi empleo en la empresa! Es un poco divertido
Josh Stevenson el

Respuestas:


17

No estoy seguro si este es el tipo de locura que estabas buscando ...

Descargo de responsabilidad : no tengo absolutamente ninguna idea de por qué querrías usar esto.

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

17

Datos

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

Soluciones 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

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

Salida:

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

Para el ejemplo final:

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

Pruébalo aquí: Stack Exchange Data Explorer


¡Muy agradable! Supongo que no podría usar esto en un escenario en el que no tengo una columna que contenga solo datos únicos como 'Nombre de usuario'. Por ejemplo, si solo tuviera nombre, apellido, ciudad.
Josh Stevenson

3
@JoshStevenson correcta, aunque añadí una manera adecuadamente loco para preservar duplicados como el ejemplo final, y luego un uno sensible.
Paul White reinstala a Monica

1
Para las GROUP BYsoluciones, también puede agregar el PK en el grupo por lista (para estar 100% seguro de que las consultas devuelven el mismo número de filas que WHERE). Suponiendo, por supuesto, que hay un PK;)
ypercubeᵀᴹ


14

"Solo por diversión" puedes usar 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;

Esto ordenará todas las filas con Townsvilleprimero ya que el caso devuelve 1if town = 'Townsville'. Todas las demás filas tendrán un 2retorno por el caso.

La with tiescláusula hace que la consulta devuelva todas las filas que son un "empate" para el último lugar en las filas devueltas. El uso top(1)en combinación con with tiesdevolverá todas las filas que tengan el mismo valor que la primera fila en la expresión utilizada en el orden por cláusula.

Tenga en cuenta que, como Martin Smith señaló en un comentario, devolverá todas las filas si solicita una ciudad que no existe en la tabla.

Si no le temes a las cosas XML de las bases de datos, puedes usar un predicado en la función de nodos ().

Préstamo de la configuración 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);

Otra versión con topy order byque realmente funciona cuando se buscan ciudades no 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

7

Tienes dos cosas diferentes aquí.

SELECT * FROM Users
WHERE town = 'Townsville'

Restringirá la cantidad de filas que regresará a aquellas donde town =Townsville

SELECT *, town('Townsville') FROM Users

Va a pasar el literal Townsvillea una función llamada town. No restringirá las filas devueltas por la consulta y, de hecho, si la función devuelve algo más que un solo valor, obtendrá un error.

Hay otras formas de restringir el número de filas que obtiene de una consulta. La cláusula HAVING, por ejemplo. Pero tiene varios otros requisitos.

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

O un INNER JOIN, aunque este es un poco extraño si no tienes una segunda mesa.

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

5

Aquí hay un ejemplo que usa una expresión de tabla común (CTE).

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

5

Bueno, podrías hacer esto:

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

Estrictamente hablando, no estás usando la cláusula WHERE


5

Aquí hay una forma idiota completamente lógica de hacerlo que aún no veo ...

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

No puedo imaginar por qué esto no fue lo primero que se sugirió. :)


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

Todas las ciudades además de Townsville serían nulas. Problema resuelto.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.