WHERE 子句的替代方案

Jos*_*son 17 sql-server

有没有办法只在SELECT列中包含某些数据的行而不使用WHERE

例如,如果我有这个:

SELECT * FROM Users
WHERE town = 'Townsville'
Run Code Online (Sandbox Code Playgroud)

有没有办法将WHERE子句实现到SELECT语句中?

就像是

SELECT *, town('Townsville') FROM Users
Run Code Online (Sandbox Code Playgroud)

这是一个奇怪的问题,但这是我的同龄人问过的问题

Mar*_*son 17

不确定这是否是您正在寻找的那种疯狂的东西......

免责声明:我完全不知道您为什么要使用它。

SELECT * 
FROM Users AS u
INNER JOIN (SELECT 'Townsville' town) towns 
  ON towns.town = u.Town;
Run Code Online (Sandbox Code Playgroud)


Pau*_*ite 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');
Run Code Online (Sandbox Code Playgroud)

替代解决方案

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
Run Code Online (Sandbox Code Playgroud)

保留重复项

-- :)
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;
Run Code Online (Sandbox Code Playgroud)

输出:

?????????????????????????
? UserName ?    Town    ?
?????????????????????????
? Charles  ? Townsville ?
? Dan      ? Townsville ?
?????????????????????????
Run Code Online (Sandbox Code Playgroud)

最后一个例子:

?????????????????????????
? UserName ?    Town    ?
?????????????????????????
? Charles  ? Townsville ?
? Charles  ? Townsville ?
? Charles  ? Townsville ?
? Charles  ? Townsville ?
? Dan      ? Townsville ?
?????????????????????????
Run Code Online (Sandbox Code Playgroud)

在这里试试: Stack Exchange Data Explorer

  • @JoshStevenson 正确,尽管我添加了一种适当的疯狂方式来保留重复项作为最后一个示例,然后是一个明智的示例。 (3认同)

Mar*_*ith 15

其它的办法。

SELECT U.*
FROM  dbo.Users U
CROSS APPLY (SELECT Town INTERSECT SELECT 'Townsville' ) CA;
Run Code Online (Sandbox Code Playgroud)

重新利用 Paul White 的示例数据


Mik*_*son 14

“只是为了好玩”,您可以使用order bywithtop(1) with ties

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

这将对所有行进行排序,Townsville因为 case 返回1if town = 'Townsville'。所有其他行将有一个2由案例返回。

with ties子句使查询返回与返回的行中最后一个位置“并列”的所有行。使用top(1)结合with ties然后将返回具有相同的值作为表达式中的第一行由子句中的顺序使用的所有行。

请注意,正如 Martin Smith 在评论中指出的那样,如果您要求表中不存在的城镇,它将返回所有行

如果您不担心数据库的 XML 事物,您可以在 nodes() 函数中使用谓词。

借用 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);
Run Code Online (Sandbox Code Playgroud)

与另一个版本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
Run Code Online (Sandbox Code Playgroud)


Ken*_*her 7

你在这里有两种不同的东西。

SELECT * FROM Users
WHERE town = 'Townsville'
Run Code Online (Sandbox Code Playgroud)

将您返回的行数限制为仅城镇 =Townsville

SELECT *, town('Townsville') FROM Users
Run Code Online (Sandbox Code Playgroud)

将把文字传递Townsville给一个名为town. 它不会限制查询返回的行,事实上,如果函数返回的不是单个值,你将得到一个错误。

还有其他方法可以限制您从查询中返回的行数。例如 HAVING 子句。但它还有其他几个要求。

SELECT town FROM Users
GROUP BY town
HAVING town = 'Townsville'
Run Code Online (Sandbox Code Playgroud)

或者一个 INNER JOIN 虽然如果你没有第二个表,这个有点奇怪。

SELECT * FROM Users
INNER JOIN (SELECT 1 col1) UselessTable
    ON Users.town = 'Townsville'
Run Code Online (Sandbox Code Playgroud)


dat*_*god 5

这是一个使用公用表表达式 (CTE) 的示例。

with Town as 
(
    select 'Townsville' as Town
)
select *
  from Users u
  join Town  t on u.Town = t.Town
Run Code Online (Sandbox Code Playgroud)


dru*_*zin 5

那么你可以这样做:

    SELECT A.* 
    FROM Users A
         INNER JOIN Users B ON A.Id = B.Id AND B.town = 'Townsville'
Run Code Online (Sandbox Code Playgroud)

严格来说,您没有使用 WHERE 子句


Eri*_*rik 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
Run Code Online (Sandbox Code Playgroud)

我无法想象为什么这不是建议的第一件事。:)