Thay thế cho mệnh đề WHERE [đã đóng]


16

Có cách nào để chỉ SELECTcác hàng với dữ liệu nhất định trong một cột mà không sử dụng WHEREkhông?

ví dụ: nếu tôi có cái này:

SELECT * FROM Users
WHERE town = 'Townsville'

Có cách nào để thực hiện WHEREmệnh đề vào SELECTtuyên bố?

cái gì đó như

SELECT *, town('Townsville') FROM Users

Đó là một câu hỏi kỳ lạ nhưng đó là câu hỏi mà tôi đã được hỏi bởi các đồng nghiệp của tôi


4
Hỏi họ tại sao họ hỏi điều này. Bối cảnh rất quan trọng.
Aaron Bertrand

@AaronBertrand, MikaelEriksson - Về cơ bản đó là một câu hỏi nhỏ về SQL tại nơi làm việc, tôi đã gặp một câu hỏi cho biết "Chọn tất cả người dùng từ bảng người dùng đến từ Townsville, mà không sử dụng mệnh đề where" Và, tôi không biết rằng có khả năng! Có lẽ tôi đang tiếp cận điều này sai cách ..?
Josh Stevenson

Cũng chỉ nói rằng bảng câu hỏi này không liên quan đến tình trạng việc làm của tôi với công ty! Đó chỉ là một niềm vui nho nhỏ
Josh Stevenson

Câu trả lời:


17

Không chắc đây có phải là thứ điên rồ mà bạn đang tìm kiếm không ....

Tuyên bố miễn trừ trách nhiệm : Tôi hoàn toàn không biết tại sao bạn lại muốn sử dụng cái này.

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

17

Dữ liệu

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

Các giải pháp thay thế

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

Giữ lại các bản sao

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

Đầu ra:

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

Ví dụ cuối cùng:

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

Hãy thử nó ở đây: Stack Exchange Data Explorer


Rất đẹp! Tôi đoán tôi sẽ không thể sử dụng điều này trong kịch bản mà tôi không có cột chỉ chứa dữ liệu duy nhất như 'Tên người dùng'? Ví dụ, nếu tôi chỉ có tên, họ, thị trấn.
Josh Stevenson

3
@JoshStevenson Đúng, mặc dù tôi đã thêm một cách phù hợp điên rồ để bảo tồn các bản sao làm ví dụ cuối cùng, và sau đó là một cách hợp lý.
Paul White phục hồi Monica

1
Đối với các GROUP BYgiải pháp, bạn cũng có thể thêm PK trong nhóm theo danh sách (để chắc chắn 100% rằng các truy vấn trả về cùng số lượng hàng như WHERE). Tất nhiên là giả sử có PK;)
ypercubeᵀᴹ


14

"Just for fun" bạn có thể sử dụng một order byvớitop(1) with ties

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

Điều này sẽ đặt hàng tất cả các hàng với Townsvilleđầu tiên kể từ khi trường hợp trả về 1nếu town = 'Townsville'. Tất cả các hàng khác sẽ có một 2trường hợp trả lại.

Các with tieskhoản làm cho các truy vấn trả lại toàn bộ hàng đó là một "cà vạt" cho vị trí cuối cùng trong hàng trả lại. Sử dụng top(1)kết hợp với with tiessau đó sẽ trả về tất cả các hàng có cùng giá trị với hàng đầu tiên trong biểu thức được sử dụng theo thứ tự theo mệnh đề.

Lưu ý, như Martin Smith đã chỉ ra trong một bình luận, nó sẽ trả về tất cả các hàng nếu bạn yêu cầu một thị trấn không tồn tại trong bảng.

Nếu bạn không sợ những thứ XML của cơ sở dữ liệu, bạn có thể sử dụng một vị từ trong hàm nút ().

Mượn thiết lập từ 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);

Một phiên bản khác có toporder bythực sự hoạt động khi tìm kiếm các thị trấn không tồn tại.

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

Bạn có hai điều khác nhau ở đây.

SELECT * FROM Users
WHERE town = 'Townsville'

Sẽ hạn chế số lượng hàng bạn quay lại chỉ những nơi có thị trấn =Townsville

SELECT *, town('Townsville') FROM Users

Sẽ vượt qua nghĩa đen Townsvillecho một chức năng được gọi là town. Nó sẽ không hạn chế các hàng được trả về bởi truy vấn và trên thực tế nếu hàm trả về bất cứ thứ gì ngoại trừ một giá trị duy nhất bạn sẽ gặp lỗi.

Có nhiều cách khác để hạn chế số lượng hàng bạn nhận được từ truy vấn. Mệnh đề HAVING chẳng hạn. Nhưng nó có một số yêu cầu khác.

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

Hoặc MỘT NGƯỜI THAM GIA mặc dù cái này hơi lạ nếu bạn không có bảng thứ hai.

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

5

Dưới đây là một ví dụ sử dụng biểu thức bảng chung (CTE).

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

5

Vâng, bạn có thể làm điều này:

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

Nói đúng ra là bạn không sử dụng mệnh đề WHERE


5

Đây là một ngu ngốc cách hoàn toàn logic để làm điều đó mà tôi không thấy chưa ....

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

Tôi không thể tưởng tượng tại sao đây không phải là điều đầu tiên được đề xuất. :)


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

Tất cả các thị trấn ngoài Townsville sẽ là null. Vấn đề được giải quyết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.