2016-05-13 2 views
2

Was ist der beste Weg? Um eine 1 oder 0 zurück zu bekommen? Oder prüfen, ob Zeilen aus einer Abfrage verfügbar sind? Ich streite für ExecuteScalar aber interessiert an anderen Antworten warum oder warum nicht.Was ist der beste Weg zu überprüfen, ob ein Datensatz in einer SQL Server-Tabelle mit C# existiert?

//using DataReader.HasRows? 
bool result = false; 

var cmd = new SqlCommand("select foo, bar from baz where id = 123", _sqlConnection, _sqlTransaction); 

cmd.CommandType = System.Data.CommandType.Text; 

using (var r = cmd.ExecuteReader()) 
{ 
    if (r != null && r.HasRows) 
    { 
     result = true; 
    } 
} 

return result; 

//or using Scalar? 
bool result = false; 

var cmd = new SqlCommand("if exists(select foo, bar from baz where id = 123) select 1 else select 0", _sqlConnection, _sqlTransaction); 

cmd.CommandType = System.Data.CommandType.Text; 

int i = (int) cmd.ExecuteScalar(); 
result = i == 1; 
return result; 
+0

Möchten Sie auch die Ergebnisse der Abfrage oder nur einen Indikator anzeigen, wenn Ergebnisse angezeigt werden? –

+0

Die dritte Option wäre 'select top 1 1 from ...' zu verwenden, was zumindest am einfachsten klingt. Sie sollten trotzdem in Ausführungsplan und Statistikausgabe schauen, wenn Sie interessiert sind, was auf der Datenbankseite passiert. –

Antwort

4

Exists ist effizienter als die Zählung, weil die Zählung alle Zeilen durchsuchen muss, um die Kriterien zu erfüllen und in die Zählung aufzunehmen, existiert nicht.

Also existiert mit ExecuteScalar besser.

Als weitere Informationen dieser Sicherung:

zu http://sqlblog.com/blogs/andrew_kelly/archive/2007/12/15/exists-vs-count-the-battle-never-ends.aspx Nach

Beide Abfragen gescannt, um die Tabelle, aber die VORHANDEN konnte gelinde bei Sie ein Partial-Scan auf die Tatsache, tun es, nachdem es zu stoppen findet die sehr erste passende Zeile. Wo als COUNT () muss jede Zeile in der gesamten Tabelle gelesen werden, um festzustellen, ob sie die Kriterien erfüllen und wie viele sind. Das sind die Schlüsselleute. Die Fähigkeit, nach der ersten Zeile zu stoppen, die die Kriterien der WHERE-Klausel erfüllt, ist , was EXISTS so effizient macht. Der Optimierer weiß von diesem Verhalten und kann das auch einrechnen. Beachten Sie, dass diese Tabellen relativ klein im Vergleich zu den meisten Datenbanken in der realen Welt sind. So würden die Zahlen der COUNT () Abfragen mehrfach auf größeren Tabellen multipliziert werden. Sie könnten leicht Hunderte von Tausenden von Reads oder mehr auf Tabellen mit Millionen von Zeilen bekommen, aber die EXISTS werden immer noch nur haben nur ein paar Lesevorgänge auf Abfragen, die einen Index verwenden können die WHERE-Klausel.

Als ein einfaches Experiment mit Adventure mit MSSQL 2012

set showplan_all on 

-- TotalSubtreeCost: 0.06216168 
select count(*) from sales.Customer 

-- TotalSubtreeCost: 0.003288537 
select 1 where exists (select * from sales.Customer) 

Siehe auch

http://sqlmag.com/t-sql/exists-vs-count

UPDATE: Auf ExecuteScalar vs ExecuteReader. Schauen Sie sich mit einem Disassembler (wie Reflector) die Implementierung von System.Data.SqlClient an.SqlCommand Methoden, zeigt etwas überraschend, sie sind irgendwie gleichwertig: beide am Ende Aufruf der internen Helfer interne SqlDataReader RunExecuteReader (CommandBehavior cmdBehavior, RunBehavior runBehavior, Bool returnStream, string-Methode, TaskCompletionSource Vervollständigung, int Timeout, Task Aufgabe, bool asyncWrite = false)

die einen SqlDataReader zurückgibt, der ExecuteReader gibt es wie es ist zurück. Während ExecuteScalar es mit einem anderen Helfer verbraucht:

private object CompleteExecuteScalar(SqlDataReader ds, bool returnSqlValue) 
{ 
    object obj2 = null; 
    try 
    { 
     if (!ds.Read() || (ds.FieldCount <= 0)) 
     { 
      return obj2; 
     } 
     if (returnSqlValue) 
     { 
      return ds.GetSqlValue(0); 
     } 
     obj2 = ds.GetValue(0); 
    } 
    finally 
    { 
     ds.Close(); 
    } 
    return obj2; 
} 

Als Randbemerkung, gleiche gilt mit MySQL Connector/NET (Der offiziellen ADO.NET Open-Source-Treiber für MySQL), intern die Methode ExecuteScalar erzeugt einen Datareader (MySqlDataReader um genau zu sein) und konsumiert es. Siehe Quelldatei /Src/Command.cs (von https://dev.mysql.com/downloads/connector/net/ oder https://github.com/mysql/mysql-connector-net).

Zusammenfassung: In Bezug auf die ExecuteScalar vs ExecuteReader beide im Overhead der Erstellung eines SqlDataReader entstehen, würde ich sagen, der Unterschied ist meist idiomatisch.

+0

Graf ist offensichtlich am wenigsten effektiv. Was können Sie über ExecuteScalar vs ExecuteReader sagen? –

+0

@AlexKudryashev Ich aktualisierte die Antwort, um einige Details zu der Sache hinzuzufügen. –

+1

Ich vermutete, dass DataReader an allen ausgewählten Operationen beteiligt ist. In diesem Fall ist .ExecuteReader() effektiver. Je weniger desto besser. –

3

würde ich mit ExecuteScalar mit einer Abfrage wie Ihr if exists gehen. Es sollte so schnell wie möglich auf dem Server und mit minimalem Netzwerkverkehr erfolgen.

1

Wenn Sie nur über die Existenz kümmern, würde ich die skalare Ansatz verwenden, sondern auch die TSQL aktualisieren zu sein:

SELECT CASE WHEN EXISTS(SELECT ...) THEN 1 ELSE 0 END 
1

ich mit einer etwas anderen Abfrage verwenden würde ExecuteScalar:

string sql = "SELECT CASE WHEN exists(select NULL from baz where id = 123) THEN 1 ELSE 0 END"; 
var cmd = new SqlCommand(sql, _sqlConnection, _sqlTransaction); 
Verwandte Themen