WHERE maddesine alternatif [kapalı]


16

SELECTBir sütunda yalnızca belirli verileri içeren satırları kullanmanın bir yolu var mı WHERE?

Örneğin, eğer bu olsaydı:

SELECT * FROM Users
WHERE town = 'Townsville'

WHEREcümlenin SELECTifadeye uygulanmasının bir yolu var mı?

gibi bir şey

SELECT *, town('Townsville') FROM Users

Bu tuhaf bir soru ama akranlarım tarafından sorulan bir soru


4
Onlara neden bunu sorduklarını sorun. Bağlam önemlidir.
Aaron Bertrand

@AaronBertrand, MikaelEriksson - Temelde işyerinde SQL ile ilgili küçük bir anket, "Townsville'den gelen kullanıcı tablosundan bir nerede cümlesi kullanmadan tüm kullanıcıları seç" diye bir soru ile karşılaştım Ve bunu bilmiyordum mümkün oldu! Belki de yanlış yola yaklaşıyorum ..?
Josh Stevenson

Ayrıca sadece bu anketin hiçbir şekilde şirket ile istihdam durumumla bağlantılı olmadığını belirtmek! Sadece eğlenceli
Josh Stevenson

Yanıtlar:


17

Bu aradığınız çılgın şey olup olmadığından emin değilim ....

Feragatname : Bunu neden kullanmak isteyeceğiniz konusunda hiçbir fikrim yok.

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

17

Veri

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

Alternatif çözümler

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

Kopyaları tutma

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

Çıktı:

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

Son örnek için:

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

Burada deneyin: Stack Exchange Data Explorer


Çok hoş! Ben sadece 'Kullanıcı adı' gibi benzersiz veri içeren bir sütun yok bir senaryoda bunu kullanamazsınız tahmin ediyorum? Örneğin sadece soyadım, soyadım, kasaba olsaydım.
Josh Stevenson

3
@JoshStevenson Doğru, yine de son örneği olarak kopyaları korumak için uygun bir deli yolu ekledim ve daha sonra da mantıklı bir örnek ekledim.
Paul White Monica'yı eski

1
İçin GROUP BYçözümleri, listenin tarafından gruptaki PK ekleyebilir (sorguları NEREDE satır sayısına eşit sayıda dönüş emin% 100 olması). Elbette bir PK olduğunu varsayarsak;)
ypercubeᵀᴹ


14

"Sadece eğlence için" order byile kullanabilirsiniztop(1) with ties

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

TownsvilleVaka 1if döndürdüğü için bu, ilk önce tüm satırları sıralar town = 'Townsville'. Diğer tüm satırlarda durum 2tarafından döndürülür.

with tiesFıkra sorgu döndürülen satırların son sıra için bir "kravat" dır tüm satırları döndürmek yapar. Daha sonra top(1)ile birlikte with tieskullanıldığında, ifadede sırayla kullanılan ifadedeki ilk satırla aynı değere sahip tüm satırlar döndürülür.

Martin Smith'in bir yorumda işaret ettiği gibi, tabloda bulunmayan bir kasaba talep ederseniz tüm satırları döndürecektir .

Veritabanlarının XML şeylerinden korkmazsanız, nodes () işlevindeki bir yüklemden yararlanabilirsiniz.

Kurulumu Paul White'dan ödünç almak.

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

Başka bir sürüm ile topve order bybu aslında mevcut şehirler için arama yaparken çalışır.

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

Burada iki farklı şeyin var.

SELECT * FROM Users
WHERE town = 'Townsville'

Geri aldığınız satırların sayısını yalnızca kasabanın =Townsville

SELECT *, town('Townsville') FROM Users

Değişmez Townsvilleolarak adlandırılan bir işleve geçecek town. Sorgu tarafından döndürülen satırları kısıtlamaz ve aslında işlev tek bir değerden başka bir şey döndürürse bir hata alırsınız.

Bir sorgudan geri aldığınız satır sayısını kısıtlamanın başka yolları da vardır. Örneğin HAVING cümlesi. Ancak bunun başka gereksinimleri de var.

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

Veya ikinci bir masanız yoksa bu biraz garip olsa da bir iç birleşim.

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

5

Ortak bir tablo ifadesi (CTE) kullanan bir örnek.

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

5

Bunu yapabilirsin:

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

Kesinlikle söylemek gerekirse WHERE yantümcesini kullanmıyorsunuz


5

İşte henüz görmedim bunu yapmak için aptalca tamamen mantıklı bir yoludur ....

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

Bunun önerilecek ilk şey olmadığını düşünemiyorum. :)


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

Townsville dışındaki tüm kasabalar boş olacaktır. Sorun çözüldü.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.