2016-04-20 22 views
0

Ich habe eine Abfrage, in der ich Ergebnisse von einer Tabelle abrufen muss, abhängig von den Parametern, die vom Benutzer auf einer vb.net-Seite angegeben werden. Die Deklaration einer Variablen wäre die ideale Lösung, aber da es sich um eine Inline-Tabellenwertfunktion handelt, konnte ich dies nicht tun, ich hatte zu anderen Fragen gelesen, dass dies in einer Multistatment-Tabellenfunktion möglich ist, aber dass die Verwendung von Diese Funktion wird nicht empfohlen.CASE innerhalb WHERE Klausel

Die folgende Abfrage wäre die ideale Lösung oder das, was ich erreichen möchte.

USE [AUDIT] 
GO 

ALTER FUNCTION [dbo].[GetCreated_LPA_Audits] 
( 
@fechaInicio nvarchar (max), 
@fechaFin nvarchar (max) 
@Period int, 
@Fecha int 

) 
RETURNS TABLE 
AS 
RETURN 
(

SELECT T1.Plant,T1.Area,T1.Location,T1.IDAudit,T1.Auditor,T1.AuditDate,T1.DueDate,T1.CreationDate 
FROM Header AS T1 inner join Audits AS T2 ON T1.IDChecklist = T2.IDChecklist 
inner join AuditGroups AS T3 ON T2.IDGroup = T3.IDGroup 

    WHERE T3.IDGroup = '2' and CreationDate is not null AND 
    CASE WHEN @Periodo = '0' THEN CreationDate>= @fechaInicio 
     WHEN @Periodo = '1' THEN DATEPART(MONTH,CreationDate)>= @Fecha 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 13 THEN 
                DATEPART(MONTH,CreationDate)>= 1 
           END 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 14 THEN 
                DATEPART(MONTH,CreationDate)>= 7 
           END 
     WHEN @Periodo = '3' THEN 
           CASE WHEN @Fecha = 15 THEN 
                DATEPART(Year,CreationDate)>= 2015 
           END 
    END 
    AND 

CASE WHEN @Periodo = '0' THEN @fechaInicio<=CreationDate 
    WHEN @Periodo = '1' THEN @Fecha<=DATEPART(MONTH,CreationDate) 
    WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 13 THEN 
                6<=DATEPART(MONTH,CreationDate) 
           END 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 14 THEN 
                12<=DATEPART(MONTH,CreationDate) 
           END 
     WHEN @Periodo = '3' THEN 
           CASE WHEN @Fecha = 15 THEN 
                DATEPART(Year,CreationDate)>= 2015 
           END 
    END 

UND In der vorherige Abfrage @fechaInicio, @fechaFin, @Periodo und @Fecha Parameter durch den Benutzer zur Verfügung gestellt werden.

Grundsätzlich, wenn @Periodo = 0 Ich brauche die Ergebnisse zu bekommen, wo CreationDate>= @fechaInicio and CreationDate<[email protected]

Wenn @Periodo = 1 ich die Ergebnisse erhalten müssen, wo DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha

Und so weiter. Hoffe, ich machte mich klar, danke im Voraus!

EDIT mit Pseudo-Code von @MatBailie, mit einigen slights Änderungen und Antworten auf seine Fragen

IF @periodo = '0' THEN 
    WHERE CreationDate >= @fechaInicio -- Copied from 1st CASE 
     AND @fechaFin <= CreationDate -- Copied from 2nd CASE 

    -- gets results from @fechaInicio to @fechaFin 
    -- i.e. results from 04/05/2016 to 04/16/2016 

IF @periodo = '1' THEN 
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha -- Copied from 1st CASE 
     AND @Fecha <= DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- In these case both conditions are the same 'cause 
    -- @Fecha is the number of a month (1 - 12) 
    -- i.e. @Fecha = 3 will get all the results of March 
    -- regardless of what it is on @fechaInicio and @fechaFin 

IF @periodo = '2' THEN 
    IF @fetcha = 13 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 1 -- Copied from 1st CASE 
      AND 6<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 
    IF @fetcha = 14 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 7 -- Copied from 1st CASE 
      AND 12<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- You never use @fetchaInicio? 
    -- You want everything in the first 6 months or last 6 months 
    -- For every year 
    -- Regardless of what is in @fetchaInicio? 

    -- Exactly!!-- 

IF @periodo = '3' THEN 
    IF @fetcha = 15 THEN 
     WHERE DATEPART(Year,CreationDate)>= 2015 -- Copied from 1st CASE 
      AND 2015 <= DATEPART(Year,CreationDate) -- Copied from 2nd CASE 

    -- 

Und was ist mit dem Fall @periodo = '2' AND @fetcha NOT IN (13,14)? Und was ist mit dem Fall @periodo = '3' AND @fetcha NOT IN (15)?

Dieser Fall würde nicht existieren, es ist auf der Client-Seite beschränkt. Wenn sie @Periodo = '2' wählen, dann haben @Fecha Werte von 1 - 12 und sonst nichts.

Same mit @Periodo = '3' dann @Fecha haben Werte von 15 oder 16, beziehe beide bis 2015 oder 2016.

+1

Mögliches Duplikat von [Bedingte Where-Klausel in Sql Server?] (Http://StackOverflow.com/questions/18629132/conditional-where-clause-in-sql-server) –

+0

'UND @VPeriodo> = @VFecha AND @VPeriodo <= @ VFecha' huh ?? .. kannst du genauer angeben, was dein Problem ist? – JamieD77

+0

@ JamieD77 Ich bekomme die Ergebnisse zwischen einem Zeitrahmen ('@ fechaInicio' und' @fechaFin') Wie auch immer die Benutzeranfrage (mit dem '@ Periodo' Parameter), muss ich die Ergebnisse von diesem TimeFrame bekommen, aber es könnte sein nach Monat, Semester oder Jahr, in '@ VPeriodo' speichere ich die gewünschte Zeitraffer (' DATEPART') in '@VFecha' es ist der Zeitrahmen, den ich brauche (dh wenn seine Monate seine 1 - 12 sind) – abichango

Antwort

0

Zuerst sind Sie richtig, Inline-TFV ist wirklich schneller. Wenn nicht zu kompliziert. Ich hätte lieber eine Anzahl von iTFVs für jeden @Periodo-Parameterwert auf der SQL-Server-Seite und wähle die richtige im Code auf der Client-Seite.

Alternativ können Sie es in einem einzigen iTVF tun

WHERE 
    @Periodo = '0' AND CreationDate>= @fechaInicio and CreationDate<[email protected] 
    OR @Periodo = '1' and DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha 
    ... 

Aber MS SQL ist bekannt ocassionally schlechte Pläne für OR-Operatoren zu bauen, die Ihre Bemühungen machen kann iTVF nutzlos zu bleiben.

+0

und Ihre letzte Aussage ist das Problem, es ist eine wachsende Tabelle, wie viele ORs wäre ein Problem? oder es geht eher um die OR-Anweisung als um die ODER-Anzahl? – abichango

0

Jedes Mal, wenn Sie die Parameter wie conditionals in Ihrer where-Klausel verwenden möchten, können Sie dieses Format folgen:

WHERE 
((@Periodo = '0' AND CreationDate 
OR (@Periodo = '1' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10)) 
OR (@Periodo = '2' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10)) 
OR (@Periodo = '3' AND @VPeriodo = CAST(DATEPART(Year,CreationDate) as Nvarchar(10))) 

Stellen Sie sicher, dass die Parameterauswertung an erster Stelle steht, da dies schnell und falsch ist, wenn die Abfrage nicht mehr den prozessintensiveren Teil der Bedingung verarbeitet.

+0

Das ist immer noch notorisch langsam. Mehrere OR-Bedingungen allein können den Optimierer ausschalten. Und das Einbetten des Suchfeldes in eine Funktion ist oft unwiederbringlich. Beide ergeben Full-Table-Scans und keine Index-Range-Scans. – MatBailie

+0

@MatBailie Jemand hat meine Frage als ein mögliches Duplikat dieser [Frage] markiert (http://stackoverflow.com/questions/18629132/conditional-where-clause-in-sql-server)! Und die Antwort ist die gleiche wie diese, ich denke, es löst mein Problem, aber wegen der Geschwindigkeit Problem, gibt es einen anderen Weg, dies zu erreichen ?? – abichango

+0

@abichango - Hier ist ein detaillierter Link, warum diese Art von Abfrage schlecht funktioniert. http://www.sommarskog.se/dyn-search-2008.html Was eine bestimmte Alternative angeht, bin ich unsicher, was genau Sie in Ihrem Kommentar zu meiner Antwort meinen. – MatBailie

1

Sie sind viel besser dran, die WHERE-Klausel neu zu organisieren, so dass das gefilterte Feld auf der linken Seite hatte und nicht innerhalb von Funktionen.

Zum Beispiel ...

WHERE 
     CreationDate >= @VPeriodo 
    AND CreationDate < CASE 
         WHEN @Periodo = '0' THEN DATEADD(DAY, 1, @VPeriodo) 
         WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, @VPeriodo) 
         WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, @VPeriodo) 
         WHEN @Periodo = '3' THEN DATEADD(YEAR, 1, @VPeriodo) 
         END 

In diesem Beispiel wird die rechte Seite ist alle skalare Konstanten. Dies bedeutet, dass Sie dann im CreationDate-Feld einen Bereichsscan durchführen können.

Auch sollte @ VPeriodo ein DATE oder eher als ein VARCHAR(MAX) sein.



EDIT: Einschließlich Reifen für VARCHARs

mit springen durch


Alle Datum YYYYMMDD im Format sein müssen, wenn die VARCHAR verwenden. Dies ist so, dass die natrual Reihenfolge der stirngs das gleiche wie die natürliche Ordnung der Daten ist ...
- '20161101'>'20161002'

Bei Verwendung andere Formate wie YYYYDDMM, es scheitert ...
- '20160111' < '20160210' Problem, in diesem Format 2. kommt Oktober NACH 1. November

WHERE 
     CreationDate >= @VPeriodo 
    AND CreationDate < CONVERT(
         NVARCHAR(8), 
         CASE 
          WHEN @Periodo = '0' THEN DATEADD(DAY, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '3' THEN DATEADD(YEAR, 1, CAST(@VPeriodo AS DATE)) 
         END, 
         112 -- Format code for ISO dates, YYYYMMDD 
        ) 



EDIT: Eine Frage an die OP nach der OP Kommentare und verändert die Frage

hier alles, was ich getan habe, ist neu ordnen, Ihren Code Pseudo-Code zu machen, was du hast geschrieben ...

IF @periodo = '0' THEN 
    WHERE CreationDate >= @fetchaInicio -- Copied from 1st CASE 
     AND @fetchaInicio <= CreationDate -- Copied from 2nd CASE 

    -- These two conditions are direct from your code 
    -- But they're the same as each other 
    -- What do you REALLY want to happen when @Periodo = '0'? 

IF @periodo = '1' THEN 
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha -- Copied from 1st CASE 
     AND @Fecha <= DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- These two conditions are direct from your code 
    -- But they're the same as each other 
    -- What do you REALLY want to happen when @Periodo = '1'? 

IF @periodo = '2' THEN 
    IF @fetcha = 13 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 1 -- Copied from 1st CASE 
      AND 6<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 
    IF @fetcha = 14 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 7 -- Copied from 1st CASE 
      AND 12<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- You never use @fetchaInicio? 
    -- You want everything in the first 6 months or last 6 months 
    -- For every year 
    -- Regardless of what is in @fetchaInicio? 

IF @periodo = '3' THEN 
    IF @fetcha = 15 THEN 
     WHERE DATEPART(Year,CreationDate)>= 2015 -- Copied from 1st CASE 
      AND DATEPART(Year,CreationDate)>= 2015 -- Copied from 2nd CASE 

    -- Both conditions are the same again, why? 
    -- You want everything from 2015 onwards, forever? 
    -- You never use @fetchaInicio? 
    -- It's always 2015? 

Und was ist mit dem Fall @periodo = '2' AND @fetcha NOT IN (13,14)? Und was ist mit dem Fall @periodo = '3' AND @fetcha NOT IN (15)?


Bitte könnten Sie meine Pseudo-Code oben nehmen und einige echte Beispiele für das, was Sie wirklich wollen, in jedem Fall zu tun?

+0

die Sache ist '@ VPeriodo' ist kein tatsächlicher Parameter, ich benutzte es, um eine Variable" zu simulieren ", um sie auf einen Wert zu egalisieren (offensichtlich funktioniert das nicht). Es ist auch 'VARCHAR', weil das' CreationDate' sein Laso 'VARCHAR' ist (So hat jemand die Tabelle entworfen) – abichango

+0

@abichango - Ich verstehe nicht, Ihre Funktionsdefinition hat eine Variable namens' @ vperiodo'. Aber unabhängig davon, ob es sich um eine Variable oder um ein Feld aus einer anderen Tabelle * (die in einem Join-Prädikat verwendet wird) * handelt, ist diese Struktur immer noch gültig. Haben Sie ein * aktuelles * Beispiel, in dem Sie diese Struktur nicht anwenden können? – MatBailie

+0

Ich denke, ich war nicht klar genug (Entschuldigung), ich habe die Variable '@ VPeriodo', aber es ist nicht in einer anderen Tabelle, noch hat es einen Wert selbst, ich habe versucht, es als eine lokale Variable zu verwenden, wo ich könnte seinen Wert gleich 'CreationDate' oder 'DATEPART (Monat, CreationDate') machen, nicht um es (wie SQL) mit diesen Variablen zu vergleichen. Die ursprüngliche Abfrage hat nicht die @ VPeriodo, weder die @VFecha und @VFecha 2, wie ich sagte, ich habe versucht, sie als Variablen zu verwenden, um Daten auf ihnen zu speichern, nicht um Daten mit ihnen zu vergleichen, macht das Sinn? Auch das ist die eigentliche Abfrage arbeitet mit. – abichango

Verwandte Themen