Alternatif klausa WHERE [tertutup]

16

Apakah ada cara untuk hanya SELECTbaris dengan data tertentu dalam kolom, tanpa menggunakan WHERE?

mis. Jika saya punya ini:

SELECT * FROM Users
WHERE town = 'Townsville'

apakah ada cara untuk mengimplementasikan WHEREklausa ke dalam SELECTpernyataan?

sesuatu seperti

SELECT *, town('Townsville') FROM Users

Ini pertanyaan aneh tapi itu pertanyaan yang pernah saya tanyakan dari teman-teman saya

Josh Stevenson
sumber
4
Tanyakan kepada mereka mengapa mereka menanyakan hal ini. Konteks itu penting.
Aaron Bertrand
@AaronBertrand, MikaelEriksson - Pada dasarnya ini adalah kuesioner kecil tentang SQL di tempat kerja, saya telah menemukan pertanyaan yang menyatakan "Pilih semua pengguna dari tabel pengguna yang berasal dari Townsville, tanpa menggunakan klausa di mana" Dan, saya tidak tahu bahwa itu mungkin! Mungkin saya mendekati ini dengan cara yang salah ..?
Josh Stevenson
Juga hanya menyatakan bahwa kuesioner ini sama sekali tidak terkait dengan status pekerjaan saya dengan perusahaan! Ini hanya sedikit bersenang
Josh Stevenson

Jawaban:

17

Tidak yakin apakah ini jenis hal gila yang Anda cari ....

Penafian : Saya sama sekali tidak tahu mengapa Anda ingin menggunakan ini.

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

Data

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

Solusi alternatif

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

Mempertahankan duplikat

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

Keluaran:

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

Untuk contoh terakhir:

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

Coba di sini: Stack Exchange Data Explorer

Paul White Reinstate Monica
sumber
Sangat bagus! Saya menduga saya tidak akan dapat menggunakan ini dalam skenario di mana saya tidak memiliki kolom yang hanya berisi data unik seperti 'Nama Pengguna'? Misal jika saya hanya punya nama depan, nama keluarga, kota.
Josh Stevenson
3
@JoshStevenson Benar, meskipun saya menambahkan cara gila yang sesuai untuk mempertahankan duplikat sebagai contoh terakhir, dan kemudian yang masuk akal.
Paul White Reinstate Monica
1
Untuk GROUP BYsolusinya, Anda juga bisa menambahkan PK dalam grup dengan daftar (agar 100% yakin bahwa kueri mengembalikan jumlah baris yang sama dengan WHERE). Dengan asumsi tentu saja ada PK;)
ypercubeᵀᴹ
14

"Hanya untuk bersenang-senang" Anda dapat menggunakan order bydengantop(1) with ties

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

Ini akan memesan semua baris dengan Townsvilleterlebih dahulu sejak kasing kembali 1jika town = 'Townsville'. Semua baris lain akan 2dikembalikan oleh kasing.

The with tiesklausul membuat query mengembalikan semua baris yang adalah "dasi" untuk tempat terakhir di baris kembali. Menggunakantop(1) kombinasi dengan with tieskemudian akan mengembalikan semua baris yang memiliki nilai yang sama dengan baris pertama dalam ekspresi yang digunakan dalam urutan oleh klausa.

Perhatikan, sebagaimana Martin Smith tunjukkan dalam komentar, itu akan mengembalikan semua baris jika Anda meminta kota yang tidak ada dalam tabel.

Jika Anda tidak takut pada hal-hal XML dari database Anda bisa menggunakan predikat dalam fungsi nodes ().

Meminjam pengaturan dari 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);

Versi lain dengan topdan order byyang benar-benar berfungsi saat mencari kota yang tidak ada.

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

Anda memiliki dua hal berbeda di sini.

SELECT * FROM Users
WHERE town = 'Townsville'

Akan membatasi jumlah baris yang Anda dapatkan kembali hanya di tempat kota =Townsville

SELECT *, town('Townsville') FROM Users

Akan melewati literal Townsvilleke fungsi yang disebuttown . Itu tidak akan membatasi baris yang dikembalikan oleh permintaan dan pada kenyataannya jika fungsi mengembalikan apa pun kecuali nilai tunggal Anda akan mendapatkan kesalahan.

Ada cara lain untuk membatasi jumlah baris yang Anda dapatkan kembali dari kueri. Klausa HAVING misalnya. Tetapi memiliki beberapa persyaratan lain.

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

Atau INNER GABUNG meskipun yang ini agak aneh jika Anda tidak memiliki meja kedua.

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

Berikut adalah contoh menggunakan ekspresi tabel umum (CTE).

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

Anda bisa melakukan ini:

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

Sebenarnya Anda tidak menggunakan klausa WHERE

druzin
sumber
5

Berikut ini adalah cara yang sepenuhnya logis bodoh untuk melakukannya yang belum saya lihat ....

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

Saya tidak bisa membayangkan mengapa ini bukan hal pertama yang disarankan. :)

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

Semua kota selain Townsville akan menjadi nol. Masalah terpecahkan.

ColorfulWind
sumber