/ / Použitie klauzuly IN s ExecuteQuery LINQ-to-SQL - sql-server, linq, linq-to-sql

Použitie klauzuly IN s ExecuteQuery LINQ-to-SQL - sql-server, linq, linq-to-sql

LINQ to SQL urobil strašnú prácu, ktorá prekladala jeden z mojich dopytov, a tak som to prepísal ručne. Problém spočíva v tom, že prepísanie nevyhnutne zahŕňa IN klauzulu a nemôžem na to, aby som si mohol zistiť, ako odovzdať kolekciu ExecuteQuery na tento účel. Jediná vec, s ktorou môžem prijať, ktorú som tu videl, je použiť string.Format na celom reťazci dopytu, aby sa k nej dostal - ale zabráni tomu, aby sa dopyt vždy skončil vo vyrovnávacej pamäti dopytu.

Aký je správny spôsob, ako to urobiť?

POZNÁMKA: Vezmite prosím na vedomie, že Používam surové SQL predané ExecuteQuery, Povedal som to v prvej vete. Povedal mi, že ju používam Contains nie je užitočné, ak neviete, ako sa miešať Contains so surovým SQL.

odpovede:

7 pre odpoveď č. 1

Parametre oceňované tabuľkou

Na stránke Cheezburger.com často potrebujeme odovzdať zoznam AssetID alebo UserID do uloženej procedúry alebo databázového dotazu.

Zlý spôsob: dynamický SQL

Jedným zo spôsobov, ako prejsť do tohto zoznamu, bolo používanie dynamického SQL.

 IEnumerable<long> assetIDs = GetAssetIDs();
var myQuery = "SELECT Name FROM Asset WHERE AssetID IN (" + assetIDs.Join(",") + ")";
return Config.GetDatabase().ExecEnumerableSql(dr=>dr.GetString("Name"), myQuery);

Je to veľmi zlé to urobiť:

  1. Dynamický SQL dáva útočníkom slabosť tým, že uľahčuje SQL injection útoky.
    Keďže zvyčajne len zlučujeme čísla, je to veľmi nepravdepodobné, ale ak začnete zlučovať reťazce dohromady, všetko, čo potrebujete, je jeden užívateľ na zadanie ";DROP TABLE Asset;SELECT " a naše stránky sú mŕtve.
  2. Uložené procedúry nemusia mať dynamický SQL, takže dotaz musí byť uložené v kóde namiesto schémy databázy DB.
  3. Pri každom spustení tohto dopytu musí byť plán dopytu prepočítaný. To môže byť pre komplikované otázky veľmi nákladné.

Má však tú výhodu, že na strane DB nie je potrebné žiadne ďalšie dekódovanie, pretože identifikačné čísla identifikátorov AssetID sú nájdené analyzátorom dopytu.

Dobrá cesta: Parametre oceňované tabuľkou

SQL Server 2008 pridáva novú schopnosť: používatelia môžu definovať typ databázy s tabuľkovou hodnotou. Väčšina ostatných typov je skalárna (vrátia iba jednu hodnotu), ale typy s hodnotou tabuľky môžu mať viac hodnôt, ak sú hodnoty tabuľkové.

Definovali sme tri typy: varchar_array, int_array, a bigint_array.

CREATE TYPE bigint_array AS TABLE (Id bigint NOT NULL PRIMARY KEY)

Obe uložené procedúry a programovo definované SQL dotazy môžu používať tieto typy tabuľkových hodnôt.

  IEnumerable<long> assetIDs = GetAssetIDs();
return Config.GetDatabase().ExecEnumerableSql(dr=>dr.GetString("Name"),
"SELECT Name FROM Asset WHERE AssetID IN (SELECT Id FROM @AssetIDs)",
new Parameter("@AssetIDs", assetIDs));

výhody

  1. Môže byť použitý v uložených procedúrach a programových SQL bez veľkého úsilia
  2. Nie je citlivý na SQL injekciu
  3. Cacheable, stabilné dopyty
  4. Nezablokuje tabuľku schém
  5. Nie je obmedzené na 8k údajov
  6. Menej práce vykonávanej serverom DB a aplikáciami Mine, pretože neexistuje zlučovanie alebo dekódovanie reťazcov CSV.
  7. štatistiky typického použitia môžu byť odvodené analyzátorom dopytu, čo môže viesť k ešte lepšiemu výkonu.

nevýhody

  1. Pracuje iba na serveroch SQL Server 2008 a novších.
  2. Predpokladá sa, že TVP sú vopred predobjednané pred vykonaním dotazu, čo znamená, že server môže odmietnuť fenomenálne veľké TVP. Ďalšie vyšetrovanie tejto povesti pokračuje.

Ďalšie čítanie

tento článok je skvelým zdrojom informácií o TVP.


3 pre odpoveď č. 2

Ak nemôžete použiť parametre v tabuľke, totoje o niečo rýchlejší ako voľba xml a zároveň vám umožní zostať ďaleko od dynamického sql: odovzdať spojený zoznam hodnôt ako parameter reťazca a analyzovať vymedzený reťazec späť na hodnoty vo vašom dopyte. prosím pozri tento článok pre pokyny na efektívne spracovanie analýzy.


2 pre odpoveď č. 3

Mám podozrenie, že ste na SQL Server 2005. Tabuľkové hodnoty neboli pridané až do roku 2008, ale stále môžete používať Typ údajov XML prechádzať množiny medzi klientom a serverom.


0 pre odpoveď č. 4

To funguje pre SQL Server 2005 (a neskôr):

create procedure IGetAListOfValues
@Ids xml -- This will recevie a List of values
as
begin
-- You can load then in a temp table or use it as a subquery:
create table #Ids (Id int);
INSERT INTO #Ids
SELECT DISTINCT params.p.value(".","int")
FROM @Ids.nodes("/params/p") as params(p);
...
end

Musíte použiť tento postup s takýmto parametrom:

exec IGetAListOfValues
@Ids = "<params> <p>1</p> <p>2</p> </params>" -- xml parameter

Funkcia uzlov používa výraz xPath. V tomto prípade je to /params/p a to "spôsobom, ktorý XML používa <params> ako root, a <p> ako prvok.

Hodnotová funkcia odovzdáva text vnútri každého p prvok do int, ale môžete ho ľahko používať s inými dátovými typmi. V tejto vzorke je DISTINCT, aby ste sa vyhli opakovaným hodnotám, ale samozrejme ju môžete odstrániť v závislosti od toho, čo chcete dosiahnuť.

Mám pomocnú (rozšírenie) metódu, ktorá konvertuje IEnumerable<T> v reťazci, ktorý vyzerá ako reťazec uvedený vvykonať príklad. Jednoducho sa dá vytvoriť a nechať to robiť prácu pre vás vždy, keď ju potrebujete. (Musíte otestovať dátový typ T a konvertovať na zodpovedajúci reťazec, ktorý môže byť analyzovaný na strane SQL Servera). Kód C # je čistejší a vaše SP sa riadia rovnakým vzorom na prijímanie parametrov (môžete poslať čo najviac zoznamov).

Jednou výhodou je, že v databáze nemusíte nič robiť, aby fungoval.

Samozrejme, nebudete musieť vytvoriť dočasný tabuľku, ako to robí v mojom príklade, ale môžete použiť dotaz priamo ako poddotaz vo vnútri IN predikát

    WHERE MyTableId IN (SELECT DISTINCT params.p.value(".","int")
FROM @Ids.nodes("/params/p") as params(p) )

0 pre odpoveď č. 5

Nie som 100% istý, že som správne pochopil problém, ale ExcuteQuery LinqToSql má preťaženie parametrov a dotaz má používať formát podobný string.Format.

Použitie tohto preťaženia je bezpečné proti SQL injection, a za scény LinqToSql transalety to používať sp_executesql s parametrami.

Tu je príklad:

string sql = "SELECT * FROM city WHERE city LIKE {0}";
db.ExecuteQuery(sql, "Lon%"); //Note that we don"t need the single quotes

Týmto spôsobom je možné využiť výhody parametrizovaných dopytov, a to aj pri použití dynamického sql.

Avšak, pokiaľ ide o použitie IN s dynamickým počtom parametrov, existujú dve možnosti:

  1. Konštruujte reťazec dynamicky a potom zadajte hodnoty ako pole, ako v:

    string sql = "SELECT * FROM city WHERE zip IN (";
    List<string> placeholders = new List<string>();
    for(int i = 0; i < zips.Length;i++)
    {
    placeholders.Add("{"+i.ToString()+"}");
    }
    sql += string.Join(",",placeholders.ToArray());
    sql += ")";
    db.ExecuteQuery(sql, zips.ToArray());
    
  2. Môžeme použiť kompaktnejší prístup pomocou metód rozšírenia Linq, ako napr

    string sql = "SELECT * FROM city WHERE zip IN ("+
    string.Join("," , zips.Select(z => "{" + zips.IndexOf(f).ToString() + "}"))
    +")";
    db.ExecuteQuery(sql, zips.ToArray());