2010-11-23 12 views
2

Ich habe 3 Tabellen:Wie vermeidet man eine große in-Klausel?

table_product (30 000 row) 
--------- 
ID 
label 

_

table_period (225 000 row) 
--------- 
ID 
date_start 
date_end 
default_price 
FK_ID_product 

und

table_special_offer (10 000 row) 
----- 
ID 
label 
date_start, 
date_end, 
special_offer_price 
FK_ID_period 

Also muss ich Daten aus all diesen Tisch laden, also hier ist es, was ich tue: 1/Daten von "table_product" wie folgt laden

select * 
from table_product 
where label like 'gun%' 

2/Lastdaten von "table_period" wie folgt

select * 
from table_period 
where FK_ID_product IN(list of all the ids selected in the 1) 

3/Lastdaten von "table_special_offer" wie diese

select * 
from table_special_offer 
where FK_ID_period IN(list of all the ids selected in the 2) 

Wie Sie die IN-Klausel in dem Punkt, vielleicht denken, 3 kann sei sehr sehr groß (wie 75 000 groß), also hatte ich eine große Chance, entweder ein Timeout zu bekommen oder so etwas wie "Ein Ausdruck-Service-Limit wurde erreicht".

Hatten Sie jemals so etwas und wie haben Sie es geschafft, es zu vermeiden?

PS: der Kontext: SQL Server 2005, .NET 2.0 (Bitte sagen Sie mir nicht meine Absicht ist schlecht, oder sollte ich „select *“ nicht, ich mein Problem nur vereinfacht, so es sich um eine etwas einfacher als 500 Seiten, die mein Geschäft beschreiben).

Danke.

Antwort

1

In endlich meine Antwort hat: Tabellenvariable (ein bisschen wie @ smirkingman Lösung aber nicht mit CTE) so:

declare @product(id int primary key,label nvarchar(max)) 
declare @period(id int primary key,date_start datetime,date_end datetime,defaultprice real) 
declare @special_offer(id int,date_start datetime,date_end datetime,special_offer_price real) 

insert into @product 
select * 
from table_product 
where label like 'gun%' 

insert into @period 
select * 
from table_period 
where exists(
select * from @product p where p.id = table_period.FK_id_product 
) 

insert into @special_offer 
select * 
from table_special_offer 
where exists(
select * from @period p where p.id = table_special_offer.fk_id_period 
) 

select * from @product 
select * from @period 
select * from @special_offer 

dies für die sQL ist, und mit C# ich benutze ExecuteReader, lesen und Nextresult der Klasse SqlDataReader

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx

Ich habe alles, was ich will: - meine Daten - i zu viele Daten nicht hat (im Gegensatz zu den Lösungen mit Join) - i nicht ausführen, die zweimal gleichen Abfrage (wie Lösung mit Unterabfrage) - ich muss meinen Mapping-Code nicht ändern (1row = 1 Business-Objekt)

1

Verwenden Sie keine explizite Liste von Werten in IN Klausel. Schreiben Sie stattdessen Ihre Abfrage wie

+1

Wird nicht wirklich helfen. – gbn

+0

das wäre eine Lösung, vielleicht mit einem existiert statt in (se @atathedev link). aber das Problem hier ist diese Lösung wäre effizient, wenn ich eine Menge Produkt/Zeitraum habe, wenn ich nur ein paar wenige wählen, mache ich besser eine explizite in. –

+0

@gbn Care zu erklären? –

0

A JOIN gibt Ihnen die gleichen Ergebnisse.

SELECT so.Col1 
     , so.Col2 
FROM table_product pt 
     INNER JOIN table_period pd ON pd.FK_ID_product = pt.ID_product 
     INNER JOIN table_special_offer so ON so.FK_ID_Period = pd.ID_Period 
WHERE pt.lable LIKE 'gun%' 
+0

@lieven, duplicata mit @atathedev, aber danke –

+0

@remi: duplizieren? Es zeigt nicht mehr, aber ich habe es eine Minute * vor * AdaTheDev gepostet. Es scheint, dass wir beide die gleiche Idee hatten. –

+0

das ist, was ich mit "duplizieren" gemeint habe, du hast die sme idee, fast den gleichen code, also danke für die antwort aber du bist schon 3 mit der gleichen lösung :) –

4

Umschalten auf Joins:

SELECT <FieldList> 
FROM Table_Product prod 
    JOIN Table_Period per ON prod.Id = per.FK_ID_Product 
    JOIN Table_Special_Offer spec ON per.ID = spec.FK_ID_Period 
WHERE prod.label LIKE 'gun%' 

Etwas, das man sich bewusst sein, sollte die Differenz von IN vs JOIN vs VORHANDEN - great article here.

+0

das Problem mit Ihrer Lösung Ich werde die Produktinformationen für jede Periode Zeile und für jedes spezielle Angebot (kartesische Produkt) bekommen. Also, wenn die Information von 1 Produkt wirklich groß ist, und ich werde 30 000 * 225 000 * 10 000 = 675 * 10^11 Zeilen anstelle von 30 000 + 225 000 + 10 000 = 265 * 10^3 Zeilen haben (die Ration ist 3 * 10^7). Vielleicht irre ich mich ... –

+0

... und ich falsch liegen, es wird 225 000 Linie in Ihrem Beispiel sein, aber in jeder Zeile erhalten wir die Produktinformationen + der Zeitraum + vielleicht das sspecial Angebot, so tun Sie denken, wenn es viele Produktinformationen gibt, kann dies zu einem Leistungsproblem führen. –

0
SELECT * 
    FROM 
     table_product tp 
     INNER JOIN table_period tper 
      ON tp.ID = tper.FK_ID_product 
     INNER JOIN table_special_offer so 
      ON tper.ID = so.FK_ID_period 
    WHERE 
     tp.label like 'gun%'" 
+0

duplicata mit @atathedev, aber danke –

0

Zuerst einige Code ...

Verwenden Sie JOIN:

SELECT 
    table_product.* --'Explicit table calls just for organisation sake' 
, table_period.* 
, table_special_offer.* 
    FROM 
     table_product 
     INNER JOIN table_period 
      ON table_product.ID = table_period.FK_ID_product 
     INNER JOIN table_special_offer 
      ON table_period.ID = table_special_offer.FK_ID_period 
    WHERE 
     tp.label like 'gun%'" 

Verwendung von IN:

SELECT 
    * 
FROM 
    table_special_offer 
WHERE FK_ID_period IN 
    (
    SELECT 
     FK_ID_period 
    FROM 
     table_period 
    WHERE FK_ID_product IN 
     (
     SELECT 
      FK_ID_product 
     FROM 
      table_product 
     WHERE label like '%gun' 
     ) AS ProductSub 
    ) AS PeriodSub 

Je nachdem, wie gut Ihre Tabellen indiziert werden beide verwendet werden können. Interne Joins, wie die anderen vorgeschlagen haben, sind definitiv effizient bei der Abfrage und geben alle Daten für die 3 Tabellen zurück. Wenn Sie nur die IDs von table_product und table_period verwenden müssen, dann kann die Verwendung der geschachtelten "IN" -Anweisungen gut für die Anpassung der Suchkriterien an indizierten Tabellen sein (Verwendung von IN kann in Ordnung sein, wenn die verwendeten Kriterien ganze Zahlen sind wie ich nehme Ihr FK_ID_product ist) .

Eine wichtige Sache zu erinnern ist, dass jede Datenbank und relationale Tabelle Setup anders handeln wird, haben Sie nicht die gleichen optimierten Ergebnisse in einer db zu einem anderen. Versuchen Sie alle Möglichkeiten zur Hand und verwenden Sie das, das für Sie am besten ist. Der Abfrageanalysator kann in Zeiten wie diesen unglaublich nützlich sein, wenn Sie die Leistung überprüfen müssen.

Ich hatte diese Situation, als wir versuchten, Kundenkonten über einen ID-Join und eine verknüpfte Tabellen-basierte Bedingung an ihre entsprechenden Adressen anzuschließen (wir hatten eine andere Tabelle, die Kunden mit bestimmter Ausrüstung zeigte, die wir eine Zeichenkettensuche durchführen mussten) auf.) Seltsamerweise war es für uns schneller, beide Methoden in der einen Abfrage zu verwenden:

- Die Abfrage mit der WHERE Desc LIKE '% Equipment%' wurde mit der IN-Klausel und an die Client-Tabelle "angefügt" dann wurde das in der Adressentabelle verbunden:

Diese Art der Abfrage (ich entschuldige mich, wenn meine Syntax ist nicht genau richtig) wurde effizienter und einfacher zu organisieren, nachdem die gesamte Abfrage komplizierter geworden war.

Hoffe, das hat geholfen - Folgen @AdaTheDev der Artikel Link, auf jeden Fall eine gute Ressource.

0

Ich würde gerne wissen, ob dies eine Verbesserung machen:

WITH products(prdid) AS (
    SELECT 
     ID 
    FROM 
     table_product 
    WHERE 
     label like 'gun%' 
), 
periods(perid) AS (
    SELECT 
     ID 
    FROM 
     table_period 
     INNER JOIN products 
      ON id = prdid 
), 
offers(offid) AS (
    SELECT 
     ID 
    FROM  
     table_special_offer 
     INNER JOIN periods 
      ON id = perid 
) 

... nur einen Vorschlag ...

Verwandte Themen