2009-07-24 8 views
2

Was ist der beste Weg zum Formatieren der folgenden SQL-Anweisung unter Berücksichtigung der Lesbarkeit und Leistung. Vielen Dank.Was ist der empfohlene Weg, um diese SQL-Anweisung zu schreiben?

sql = (char *)" SELECT * ,rowid FROM tblEvent_basic " 
        " WHERE " 
        " service_id = ? AND " 
        " (" 
        " (start_time >= ? AND start_time <= ?) OR " 
        " (end_time > ? AND end_time <?) OR " 
        " (start_time < ? AND end_time > ?)" 
        ")" 
        " ORDER by start_time ASC"; 

EDIT: 1.sqlite3 Datenbank-Engine; mit C-API, läuft auf MIPS24K 250M Embedded-CPU.

2.Der zweite, vierte, sechste Parameter ist derselbe und der dritte, fünfte, siebte ist derselbe.

rc = sqlite3_bind_int(sql_stmt,1,service_id); 
    rc = sqlite3_bind_text(sql_stmt,2,ts.start, 5, SQLITE_TRANSIENT); 
    rc = sqlite3_bind_text(sql_stmt,3,ts.end , 5, SQLITE_TRANSIENT); 
    rc = sqlite3_bind_text(sql_stmt,4,ts.start, 5, SQLITE_TRANSIENT); 
    rc = sqlite3_bind_text(sql_stmt,5,ts.end , 5, SQLITE_TRANSIENT); 
    rc = sqlite3_bind_text(sql_stmt,6,ts.start, 5, SQLITE_TRANSIENT); 
    rc = sqlite3_bind_text(sql_stmt,7,ts.end , 5, SQLITE_TRANSIENT); 
+0

Welche Abfrage-Engine verwenden Sie? Wir könnten konkretere Empfehlungen geben, wenn wir es wüssten. – SqlRyan

+0

Ist das C/C++? Sie könnten einige Backslash-Zeilenumbrüche verwenden, um diese Zeichenfolge zu zerhacken und einige Anführungszeichen zu speichern. – maxwellb

+0

Können Sie angeben, welche Parameter identisch sind? (Ich nehme an, dass der dritte Ausdruck im OR die gleichen Parameter wie die ersten beiden hat) –

Antwort

6

Ihr zeitlicher Zustand ist zur Zeit:

  " (start_time >= ? AND start_time <= ?) OR " 
      " (end_time > ? AND end_time <?) OR " 
      " (start_time < ? AND end_time > ?)" 

Wir haben sofort die Lesbarkeit (in konstanter Breite Schriftart) verbessern können mit einigen Räumen:

  " (start_time >= ? AND start_time <= ?) OR " 
      " (end_time > ? AND end_time < ?) OR " 
      " (start_time < ? AND end_time > ?)" 

Und aus dem Kommentar, wissen wir, dass die gleiche Der Wert wird an die Platzhalter 1, 3, 5 übergeben und ein anderer Wert wird an die Platzhalter 2, 4, 6 übergeben (aber alle erhalten auch den gleichen Wert). Weiter, wenn wir diese Zeiten t1 und t2 aufrufen, dann können wir davon ausgehen, dass t1 <= t2.

Also, was ist dieses Kriterium?

  • Startzeit in dem Bereich t1..t2
  • Endzeit in dem Bereich fällt t1..t2
  • Startzeit früher als t1 und Endzeit später ist als t2

Dies ist ein Überlappungen Kriterium auf die harte Art und Weise geschrieben - es sollte ersetzt werden durch: ein hier der Ausnahme, dass Platzhalter

  "(start_time <= ? AND end_time >= ?)" 

entspricht zu t2 und Platzhalter zwei entspricht t1. Wenn Sie nicht möchten, dass Ereignisse, die den Zeitbereich erfüllen, gezählt werden (dh Sie möchten kein Ereignis im Zeitpunkt t1 oder ein Ereignis, das zum Zeitpunkt t2 gestartet wurde) zählen, ändern Sie den Wert ">=" 'und' <= 'in' > 'bzw.' < '.

Dies ist die Standardmethode zum Schreiben des Überlappungsprädikats beim Einschließen der Endzeiten. Die Bedingung ist viel einfacher - keine ODER-Begriffe - und ist zuverlässig. Es wird schneller sein, da das Optimierungsprogramm weniger Arbeit zu erledigen hat und möglicherweise die Ausführungs-Engine weniger anzuwendende Kriterien hat. (Ein wirklich guter Optimierer könnte die Äquivalenz der 2-Platzhalter- und 6-Platzhalter-Versionen ausmachen, aber ich würde nicht darauf wetten, dass dies der Fall ist - nicht zuletzt, weil der Optimierer nicht erkennen kann, dass die Platzhalter 1,3,5 sein werden Gleichermaßen sind die Platzhalter 2,4,6 nicht identisch, das kann nur bestimmt werden, wenn es bei der Ausführung der Anweisung erneut optimiert wurde.

+0

+1 Zur Beantwortung der nicht gestellten Frage. Mir gefällt, dass Sie die Relevanz einiger ORs optimiert haben. Sonst braucht man start_time überhaupt nicht. – maxwellb

+0

Oh, und angenommen, dass die Endzeit am Ende der Zeit inklusiv sein kann. Ich vermute, dass dies das ist, was OP anstrebte, aber es begann im falschen Winkel. – maxwellb

5

Für den Anfang können Sie zwischen> statt = verwenden und < =. Dies würde die Abfrage viel lesbarer machen, ohne Auswirkungen auf die Leistung. Was die Optimierung der Abfrage für die Leistung betrifft, sollten Sie die Verwendung des Äquivalents des EXPLAIN-Plans in Ihrer Datenbank untersuchen, um einige Hinweise darauf zu geben, wo Ihre Abfrage die meiste Zeit benötigt.

+3

+1 auf EXPLAIN. Lediglich der Bereich "start_time" ist jedoch inklusiv, so dass die Lesbarkeit nicht sehr verbessert wird. – Thorarin

3

StartTime und EndTime sollten beide indiziert sein - da die gesamte Filterung und Sortierung basierend auf diesen Werten erfolgt, ist das wichtig.

Ich würde auch zweite mit der BETWEEN-Anweisung, wenn Ihre SQL-Engine es unterstützt. BETWEEN ist jedoch normalerweise inklusiv (es ist sowieso in SQL Server), also funktioniert es möglicherweise nur für Ihren ersten Datumsfilter, da die anderen < und> verwenden.

2

Ich empfehle nicht mit "SELECT *", das ist in der Regel CPU/Zeit/was auch immer mehr verbrauchen als explizit die Felder, die Sie haben möchten, und es ist lesbarer für jemand anderen, weil Sie sich nicht erinnern müssen, welche sind die Felder in der Tabelle enthalten.

+1 für die BETWEEN wird es Auswirkungen auf die Leistung, so dass Ihre Abfrage schneller.

+0

Wie macht BETWEEN die Abfrage schneller? – Thilo

0

Hmm ... Erstens, keine hartcodierten Abfragen im Quellcode. Wenn Sie jedoch wirklich benötigen, überprüfen Sie, ob Ihre Programmiersprache der Wahl mehrzeilige Strings oder Blöcke unterstützt (oder wie immer Sie das nennen mögen). Zum Beispiel in Ruby:

sql = <<BLOCK 

SELECT * ,rowid FROM tblEvent_basic 
WHERE 
service_id = ? AND 
(
(start_time >= ? AND start_time <= ?) OR 
(end_time >= ? AND end_time <?) OR 
(start_time < ? AND end_time > ?) 
) 
ORDER by start_time ASC; 

BLOCK 

oder in C#:

sql = @"SELECT * ,rowid FROM tblEvent_basic 
WHERE 
service_id = ? AND 
(
(start_time >= ? AND start_time <= ?) OR 
(end_time >= ? AND end_time <?) OR 
(start_time < ? AND end_time > ?) 
) 
ORDER by start_time ASC;" 
+1

Ich habe gelesen, dass einige Abfrageoptimierer mit literalen Anfragen effizienter als mit parametrisierten sind. Ich habe das einmal über Oracle gelesen. Habe es nicht verifiziert, aber es war zum Nachdenken. Könnte es mit Caching-Mechanismen zu tun haben? – maxwellb

+0

Die Änderung, die hier vorgeschlagen wurde (das Verschieben des SQL aus dem Quellcode) hat nichts damit zu tun, es mehr oder weniger literal zu machen. In beiden Fällen ist es vollständig mit Bind-Variablen parametrisiert. Da literale Abfragen manchmal effizienter sind, kann dies der Fall sein, wenn die Daten signifikant verzerrt sind, und die Verwendung eines Literals zur Überprüfung von Histogramminformationen kann zu einem effizienteren Abfrageplan führen als die verallgemeinerte Version für eine Bind-Variable. – Thilo

+0

Manchmal passiert es, dass der zwischengespeicherte Abfrageplan für eine parametrisierte Abfrage die erste Gruppe von Parametern verwendet, die aufgrund dieser speziellen Parameter zu einem pathologisch schlechten Abfrageplan führen kann, und verwendet diesen Abfrageplan dann weiterhin für alle nachfolgenden Parameter. – thelsdj

1

Formating (Zeilenumbrüche, Einrückungen, ...) wird keine Auswirkungen auf die Leistung haben. Außer, wenn Sie Tonnen (ich meine wie Tausende/Millionen unnötige Leerzeichen) Leerzeichen hinzufügen, die Abfrageübertragung erheblich verzögern können. Der Compiler kompiliert die gesamte Abfrage trotzdem als einzelne Konstante.

+0

Lesbarkeit. meh. Ich denke, das war eher eine persönliche Vorliebe – maxwellb

+1

Natürlich ist es, und das ist der Grund, warum ich betone, dass die Formatierung einer Abfrage, um lesbarer zu sein, keinen Einfluss auf die Leistung haben wird. – maciejkow

0

Haben Sie irgendwelche Einschränkungen, die Sie den Parametern auferlegen? Sie können die Abfrage auch optimieren, indem Sie unnötige Parameter entfernen, wenn Sie Einschränkungen haben, die die aufgeführten Spezifikationen doppelt machen.

Wie, Ihre Abfrage ist logisch äquivalent zu:

"SELECT *,rowid FROM tblEvent_basic WHERE service_id = ? AND (\ 
    end_time != ? AND \ 
    end_time > ?) \ 
ORDER BY start_time ASC;" 

mit

rc = sqlite3_bind_int(sql_stmt,1,service_id); 
rc = sqlite3_bind_text(sql_stmt,2,ts.end, 5, SQLITE_TRANSIENT); 
rc = sqlite3_bind_text(sql_stmt,3,ts.start, 5, SQLITE_TRANSIENT); 

.. mit der Annahme, dass < = ts.end ts.start. Anwendungslogik kann oft die Arbeit der Datenbank-Engine speichern, wenn Sie gut definierte Parameter haben.

+0

Umgekehrt können Sie die Abfrage auch optimieren, indem Sie redundante Einschränkungen einführen. Es hängt alles davon ab, welche Indizes vorhanden sind. Im Falle eines "Select *" benötigt er sowieso alle Spaltendaten, damit die Prüfungen nicht teuer werden. – Thilo

2

Geben Sie an, welche Spalten Sie benötigen, wählen Sie * sollte nicht im Produktionscode verwendet werden. Durch das Senden nur der Spalten, die Sie benötigen, wird die Leistung verbessert. Momentan wird rowid zweimal zurückgegeben und somit ist zumindest ein Teil dessen, was Sie zurückgeben, eine Verschwendung von Datenbank- und Netzwerkressourcen.