2017-04-01 2 views
0

C# -Programmierer hier und ich habe beauftragt, eine lange SQL Server-Prozedur in einer unserer alten Anwendungen zu bearbeiten, die so leistungsorientiert und effizient wie möglich ausgeführt werden muss. Ich würde mich über eine Bestätigung freuen, wenn meine Route die beste für das vorliegende Szenario ist.SQL Server: Hilfe zu optimieren meine Abfrage

SZENARIO

Es ist ein unorthodoxer ein aufgrund der Konstruktion sowohl Code und DB weise von den ursprünglichen Designern. Für ein Suchraster muss ich in tbl.docs sehen, ob ein "Garantiedokument" generiert wurde oder nicht, basierend auf der vom Benutzer gewählten Option und dem Verkettungswert des Strings/Wortes "Warranty" und dem tbl.warranty.warrantyId Wert selbst.

Dieser Wert von 'Garantie' + tbl.warranty.warrantyId entspricht dem tbl.docs.name Spaltenwert und erscheint leider meine einzige Möglichkeit, die beiden Tabellen beizutreten/zu verbinden.

Garantie-Dokumente können mehrmals pro Garantie generiert werden.

Parameter @IsGenerated ist die Benutzeroption, die der Endbenutzer auswählt, um die Filterung zu aktivieren oder nicht.

Hier einige bevölkerten Daten zur Erläuterung:

tbl.warranty:

| warrantyId | name  | createdDate    | 
|------------|-------------|-------------------------| 
| 456  | Warranty 1 | 2002-04-01 12:14:53.330 | 
| 1000  | Warranty 2 | 2002-04-01 12:14:53.327 | 
| 1701  | Warranty 3 | 2002-04-01 12:14:53.333 | 
| 2456  | Warranty 4 | 2002-04-01 12:14:53.327 | 
| 3556  | Warranty 5 | 2002-04-01 12:14:53.330 | 

tbl.docs:

| docId  | name   | createdDate    | 
|------------|--------------|-------------------------| 
| 1   | Warranty1000 | 2006-11-25 13:33:31.093 | 
| 2   | Warranty456 | 2015-02-11 13:33:31.100 | 
| 3   | Warranty1000 | 2012-05-17 13:33:31.097 | 
| 4   | Warranty1000 | 2017-01-11 13:33:31.097 | 
| 5   | Warranty1701 | 2017-03-14 13:33:31.100 | 
| 6   | Warranty456 | 2017-03-15 13:33:31.100 | 

So können Sie sehen:

  • warrantyId 1000 ha s dreimal erzeugt worden
  • warrantyId 456 zweimal erzeugt wurde
  • Garantien mit warrantyIds 3556 und 2456 wurden überhaupt nicht erzeugt, so weiter ...

gewünschte Aktion (en)/RESULT (s)

Benutzer kann wählen, keine Filteroptionen zu wählen, [Ich muss alle Garantiesätze und die erzeugte Zeit (tbl.docs.createdDate) anzeigen, aber mit einem Null tbl.docs.createdDate (generateddate wenn Sie), wenn es nie angefordert worden ist, um zu erzeugen, oder der JÜNGSTE Rekord, der auf MA basiert x tbl.docs.createdDate (generatedDate), wenn es]

| warrantyId | name   | generatedDate   | 
|------------|--------------|-------------------------| 
| 456  | Warranty 1 | 2017-03-15 13:33:31.100 | 
| 1000  | Warranty 2 | 2017-01-11 13:33:31.097 | 
| 1701  | Warranty 3 | 2017-03-14 13:33:31.100 | 
| 2456  | Warranty 4 | NULL     | 
| 3556  | Warranty 5 | NULL     | 

Benutzer auf Garantien filtern kann, hat die Dokumentation für generiert wurden, [ich alle Garantie Aufzeichnungen anzeigen müssen, dass ein doc Rekord auf der Basis haben Zeichenfolge/Wort "Garantie" und die Garantie-ID entspricht dem Wert von docs.name. Garantien, die mehrere Dokumente erzeugt haben, ich brauche nur das jüngste Rekord erzeugt]

| warrantyId | name   | generatedDate   | 
|------------|--------------|-------------------------| 
| 456  | Warranty 1 | 2017-03-15 13:33:31.100 | 
| 1000  | Warranty 2 | 2017-01-11 13:33:31.097 | 
| 1701  | Warranty 3 | 2017-03-14 13:33:31.100 | 

Benutzer filtern können auf Garantien, die Dokumente sind nicht für generiert wurde, [Ich brauche alle Garantie Datensätze anzuzeigen, die haven‘ t hatte einen doc-generierten Datensatz basierend auf der Zeichenkette/dem Wort "Warranty" und der WarrantyId-gleichwertigen Dokumentation.name]

| warrantyId | name   | generatedDate   | 
|------------|--------------|-------------------------| 
| 2456  | Warranty 4 | NULL     | 
| 3556  | Warranty 5 | NULL     | 

Das ist mein Stab an sie. Ich habe mir gedacht, eine temporäre Tabelle basierend auf der Docs-Tabelle zu erstellen und diese in meiner Abfrage zu verwenden, ist der beste Weg für Effizienz und vermeidet, dass diese Abfrage mehr als das, was bereits ist, unterdrückt wird.

IF OBJECT_ID('tempdb..#tmpDocs') IS NOT NULL 
    DROP TABLE #tmpDocs 
GO 

CREATE TABLE #tmpDocs 
(
    createdDate datetime not null, 
    name varchar(30) not null 
) 

INSERT INTO #tmpDocs (createdDate, name) 
    SELECT createdDate, name 
    FROM docs 

DECLARE @IsGenerated BIT 
SET @IsGenerated = 0 

SELECT 
    w.warrantyId, 
    w.name, 
    (SELECT TOP 1 d.createdDate 
    FROM #tmpDocs d 
    WHERE d.name = CONCAT('Warranty', w.warrantyId) 
    ORDER BY d.createdDate DESC) AS [generatedDate] 
FROM 
    warranty w 
WHERE 
    (@IsGenerated IS NULL OR @IsGenerated = 0 
    OR EXISTS (SELECT * 
       FROM #tmpDocs d 
       WHERE d.name = CONCAT('Warranty', w.warrantyId))) 
        AND (@IsGenerated IS NULL OR @IsGenerated = 1 OR 
         NOT EXISTS (SELECT * 
            FROM #tmpDocs d 
            WHERE d.name = CONCAT('Warranty', w.warrantyId) 
        ) 
       ) 
    ) 

EDITED - VERSUCH # 2

select 
    w.warrantyId, 
    w.name, 
    d.createdDate as [generatedDate] 
from warranty w 
left join #tmpDocs d 
    on d.name = concat('Warranty', w.warrantyId) 
    AND d.createdDate = (SELECT top 1 d.createdDate 
         FROM  #tmpDocs d 
         WHERE d.name = CONCAT('Warranty', w.warrantyId) 
         ORDER BY d.createdDate desc) 
WHERE 
    (@IsGenerated IS NULL OR @IsGenerated = 0 
    OR EXISTS (SELECT * 
       FROM #tmpDocs d 
       WHERE d.name = CONCAT('Warranty', w.warrantyId)) 
    ) 
    AND (@IsGenerated IS NULL OR @IsGenerated = 1 
    OR NOT EXISTS (SELECT * 
        FROM #tmpDocs d 
        WHERE d.name = CONCAT('Warranty', w.warrantyId) 
     ) 
    ) 
group by w.warrantyId, d.createdDate, w.name 

Die Ausführungszeiten sind die gleichen aber ich nehme an, weil meine kleine Datenmenge I mit gerade arbeite:

(5 Zeile (n) betroffen)

Ausführungszeit des SQL-Servers: CPU-Zeit = 0 ms, verstrichene Zeit = 0 ms.

(5 row (s) affected)

SQL Server-Ausführungszeiten: CPU-Zeit = 0 ms, verstrichene Zeit = 0 ms.

Vielen Dank für das Lesen und so weiter. Wenn es eine alternative Route gibt (ohne CTE, da dies nur ein einfacher Aspekt des Prozesses ist, den ich bearbeiten werde, habe ich nicht die Zeit, das Ganze zu refaktorisieren zu der Zeit), würde ich sehr offen sein, es zu hören. Die Ausführung dieses Verfahrens kann es sich nicht leisten, stecken zu bleiben.

Antwort

0

Dies ist definitiv nicht optimal. Wenn Sie eine Stromversorgung haben, besteht die beste Lösung darin, die doc-Tabelle zu aktualisieren und die Garantiekennung als separate Spalte hinzuzufügen, mit demselben Datentyp wie die Garantiekennung der Garantietabelle. Wenn das keine Option ist, wäre eine andere Lösung, beide Abfragen (eine für Dokumente und eine für Garantien) zu lösen und die Anwendung die IN-Klausel erzeugen zu lassen, um die Doc-Namen zu enthalten.

Wie für die Filterung, die besser im Anwendungscode behandelt werden würde.

Die folgende Abfrage wird Sie bekommen, was Sie brauchen, obwohl nicht zu sicher auf die Leistung, ohne tatsächlich die Abfrage gegen die db selbst ausführen zu können.

SELECT 
    w.warrantyId, 
    MAX(w.name) as name, 
    MAX(d.createdDate) as generatedDate 
FROM warranty w 
LEFT JOIN docs d on d.Name = CONCAT('Warranty', w.warrantyId) 
GROUP BY w.warrantyId 
+0

Ich schätze die Antwort sehr. Ich kann die Struktur der Tabelle nicht bearbeiten, da mehr als nur Garantiedokumente generiert werden usw. All dies filtert durch den gespeicherten Prozess, und das Bearbeiten des Entwurfs/Arbeitsablaufs von diesem über die Anwendungsseite würde es über die zugewiesene Zeit hinaus dauern für dieses Projekt gegeben. Ich bin im Grunde gezwungen, mit der Struktur vor Ort zu arbeiten. – theoryTim

+0

Nicht sicher, wie viele SPs Sie arbeiten, aber die obige Abfrage sollte Ihnen die Ergebnisse, die Sie suchen. in Ihren Ergebnissen suchten Sie nur nach dem neuesten Dokument, oder wollten Sie eine Garantie pro Dokument haben? – Vee

+0

Ist es auch möglich, Indizes für die Tabellen zu ändern? Sie haben diese in der ursprünglichen Frage nicht erwähnt, und der schlechte/gute/gute Index ist normalerweise der beste Ort, um mit der Optimierung zu beginnen. – Vee