Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Δυναμικό WHERE IN() χωρίς δυναμικη SQL

Îåêßíçóå áðü ôï ìÝëïò cap. Τελευταία δημοσίευση από το μέλος George J. Capnias στις 04-06-2005, 02:04. Υπάρχουν 16 απαντήσεις.
Σελίδα 1 από 2 (17 εγγραφές)   1 2 >
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  01-06-2005, 20:52 2382

    Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Από τότε που γράφτηκε το Δυναμικό pass SQL με χρήση EXEC και μετά από αυτά που μας...έσουρε (με το δίκιο του) ο Pkanavos, έψαχνα να βρώ τροπο να εκτελέσω ένα αναθεματισμένο select έχοντας στο χέρι μου μόνο ένα delimited string με IDs. Αυτό που έκανα μέχρι τώρα ήταν το εξής: Εφτιαχνα μια stored procedure η οποία δεχόταν σαν παράμετρο ένα varchar(1000) π.χ., και μετά έτρεχα ένα EXEC που ήταν π.χ. κάπως έτσι:

    EXEC 'SELECT a,b,c FROM mytable WHERE myfield IN (' + @myparam +')'

    Ωσπου άναψε ένα λαμπάκι πάνω από το κεφάλι μου και σκέφτηκα οτι μπορώ να το κάνω κι αλλιώς. Για την ακρίβεια άλλος το σκέφτηκε, εγώ το...ανακάλυψα.

    Εδω λοιπόν μπορείτε να βρείτε ένα σχετικό αρθράκι. Παραθέτω τον κώδικα δημιουργίας μιας UDF, όπως αναφέρει το άρθρο, που δέχεται ένα delimited string με αριθμους (υποθέτουμε οτι έχουμε ints), και δημιουργεί ένα πίνακα μιας στήλης με τους αριθμούς αυτούς έτσι ωστε να μπορέσουμε να τον κάνουμε join στον αρχικό:

    Να σημειώσω οτι την παραθέτω αυτούσια, όπως αναφέρεται στο άρθρο. Σιγουρα σηκώνει βελτιώσεις, αλλα δεν ήθελα να την πειράξω.

    Create Function dbo.CsvToInt ( @Array varchar(1000))
    returns @IntTable table
     (IntValue int)
    AS
    begin

     declare @separator char(1)
     set @separator = ','

     declare @separator_position int
     declare @array_value varchar(1000)
     
     set @array = @array + ','
     
     while patindex('%,%' , @array) <> 0
     begin
     
       select @separator_position =  patindex('%,%' , @array)
       select @array_value = left(@array, @separator_position - 1)
     
      Insert @IntTable
      Values (Cast(@array_value as int))

       select @array = stuff(@array, 1, @separator_position, '')
     end

     return
    end

    Εχοντας αυτή την function μπορεί κανείς πρακτικά να καταργήσει το dynamic SQL του μια και μπορεί πλέον να κάνει το εξής:

    SELECT a, b, c
    FROM mytable
    INNER JOIN dbo.CsvToInt ('1,34,433,3') tblkeys
    ΟΝ mytable.myid = tblkeys.IntValue

    Χρήσιμο. Βεβαια παραμένει το πρόβλημα της απόδοσης, αλλά τουλάχιστον δεν έχουμε EXEC statements.




    Σωτήρης Φιλιππίδης

    DotSee Web Services

    View Sotiris Filippidis's profile on LinkedIn

    DotNetNuke them!
  •  02-06-2005, 06:56 2390 σε απάντηση της 2382

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Ωραίος!


    while (!dead) learn();
  •  02-06-2005, 08:45 2391 σε απάντηση της 2382

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Όντως ενδιαφέρον ...
    Μπράβο !!!


    Πάνος Αβραμίδης
  •  02-06-2005, 10:11 2392 σε απάντηση της 2382

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Μη σε απασχολεί τόσο το πρόβλημα της απόδοσης. Ο optimizer θα πάρει τον πίνακα με τις τιμές και θα κάνει ένα nested join, δηλαδή για κάθε τιμή που επιστρέφει η CsvToInt, θα ψάξει τον mytable. Αυτό θα έκανε έτσι κι αλλιώς αν έκανες join με ένα άλλο πίνακα με λίγες εγγραφές αντί για το function.

    Αυτό που παίρνει ελάχιστη βελτίωση είναι το patindex. Επειδή εδώ δεν ψάχνεις για κάποιο pattern μπορείς να χρησιμοποιήσεις το charindex. Επειδή όμως το string που που περνάς είναι συνήθως μικρό, δεν θα δεις καμμία διαφορά.


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  02-06-2005, 10:50 2394 σε απάντηση της 2382

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Λίγο unoptimized , χρησιμοποιεί TEXT Datatype αντί για VARCHAR ώστε να ξεπεράσει το μόνιμο πρόβλημα με το limitation του varchar(8000) . 
    Από την άλλη το
    TEXT είναι λίγο δύσχρηστο στα assignments ( Δεν μπορείς να ορίσεις local μεταβλητές ) αλλά αν το περνάς από SP παίζει μια χαρά .
    Στη θέση του
    TEXT μπορείτε βέβαια να περάσετε και VARCHAR απλά θα περάσουν max. 8000 χαρακτήρες.


    CREATE Function dbo.fn_TableFromDelimitedString (@string TEXT,@delimiter VARCHAR(10)=',' )
    RETURNS @Table TABLE (Id INT)
    AS
    BEGIN

    DECLARE @Found      INT
    DECLARE @FoundEnd     INT
    DECLARE @Id        INT

    IF DATALENGTH(@string)>0 BEGIN
        
        SET @Found=1
        SET @FoundEnd=0
        
        WHILE @FoundEnd<DATALENGTH(@string)  BEGIN
            SET @FoundEnd=CHARINDEX(@delimiter,SUBSTRING(@string,@Found,DATALENGTH(@string)),0)

            IF @FoundEnd = 0 BEGIN
                SET @FoundEnd=DATALENGTH(@string)+1
            END
            
            SET @Id=SUBSTRING(@string,@Found,@FoundEnd-1)  

            INSERT INTO @Table VALUES(@ID)
            
            SET @Found=@Found+@FoundEnd
        END

    END    

    RETURN

    END


     






    Sex is like programing; One mistake, and you will have to support it for the rest of your life…
  •  02-06-2005, 10:56 2395 σε απάντηση της 2394

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Σωστό! Ακόμα καλύτερο!
    Εγώ να είσαι σίγουρος οτι θα το αξιοποιήσω Smile

    Σωτήρης Φιλιππίδης

    DotSee Web Services

    View Sotiris Filippidis's profile on LinkedIn

    DotNetNuke them!
  •  02-06-2005, 11:55 2399 σε απάντηση της 2392

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    YesYesYesYes
    while (!dead) learn();
  •  02-06-2005, 14:46 2403 σε απάντηση της 2394

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Παιδιά, μήπως ξεφύγαμε? Πόσες τιμές θέλετε να περάσετε στο string? Το varchar χωράει 1600 τετραψήφιους αριθμούς, σκέφτεστε σοβαρά να περάσετε τόσους?

    Άσε που αν περάσεις τόσους αριθμούς, το nested join που θα χρησιμοποιήσει ο optimizer θα είναι ααααρρρργγγγγγόοοοοοοο! Μία λύση θα ήταν να κάνεις το ID Primary Key για να μπορέσει ο optimizer να χρησιμοποιήσει κάποιο άλλο join, π.χ. merge, αλλά και πάλι, ξεφύγαμε!

    Από την άλλη, αμφιβάλλω αν μπορεί να γίνει πιο optimized η fn_TableFromDelimitedString. Απλά, η T-SQL σε αυτή την έκδοση δεν είναι η κατάλληλη γλώσσα για να παίζεις με strings.

    Ax, πότε θα μπει στην T-SQL και ένα array type!

    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  02-06-2005, 14:50 2404 σε απάντηση της 2403

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Βρε Πανο, δεν τα ξαναλέγαμε; Μερικές φορές δεν είναι στο χέρι μας...υπάρχουν ακραίες περιπτώσεις που αυτό *πρεπει* να γίνει (επέμβαση σε ήδη υπάρχοντα πράγματα, quick fixes ωσπου να βρεθεί χρόνος για κάτι πιό optimized, κλπ).

    Δεν είναι η καλύτερη λύση αλλά σε ορισμένες περιπτώσεις (τονίζω το "ορισμένες"), αποτελεί πιστεύω λύση.


    Σωτήρης Φιλιππίδης

    DotSee Web Services

    View Sotiris Filippidis's profile on LinkedIn

    DotNetNuke them!
  •  02-06-2005, 15:02 2405 σε απάντηση της 2404

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Δηλαδή, πραγματικά χρειάζεται να περάσεις τόσες τιμές?

    Indifferent

    Αποπληξία!


    Στην περίπτωση αυτή επιβάλεται το ID να είναι primary key. Ίσως μάλιστα να πρέπει να χρησιμοποιηθεί και temporary table αν οι τιμές είναι πάρα πολλές. Εδώ πλέον μιλάμε για τρελές, εξωτικές λύσεις.
    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  02-06-2005, 15:13 2406 σε απάντηση της 2405

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Να διορθώσω: Αυτό που εννοώ λέγοντας οτι είναι αξια λόγου η λύση, είναι ακριβώς για αυτές τις "εξωτικές" καταστάσεις. Οχι, δεν το χρειάζομαι, ισως και να μην το χρειαστώ, σε περίπτωση όμως που θα το χρειαστώ ξέρω και ΤΙ χρειάζεται να κάνω και ΤΙ θα μου κοστίσει αυτό που θα κάνω...

    Προσωπικά είμαι κατά των λύσεων-μπαλωμάτων και είμαι ο πρώτος που τις πολεμάει όσο μπορεί. Αλλά ορισμένες φορές, είναι do or die...και (αν και ξέρω οτι θα με βαρέσετε για αυτο που θα πω) θεωρώ οτι μερικές φορές έχει και καλό λόγο να είναι έτσι.


    Σωτήρης Φιλιππίδης

    DotSee Web Services

    View Sotiris Filippidis's profile on LinkedIn

    DotNetNuke them!
  •  02-06-2005, 15:24 2407 σε απάντηση της 2406

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Λοιπόν, θα μπορούσες να βάλεις ένα έλεγχο στο stored procedure για τον αριθμό των ID που προκύψανε. Αν είναι πολύ μεγάλος θα μπορούσες να χρησιμοποιήσεις το ίδιο query με hint για merge join. Προσοχή όμως, το merge join είναι γρηγορότερο μόνο αν και οι δύο στήλες που συμμετέχουν είναι ταξινομημένες με τον ίδιο τρόπο. Αλλιώς τις ταξινομεί με αποτέλεσμα να καθυστερεί περισσότερο.

    Έχε το κι αυτό υπόψη να το χρησιμοποιήσεις αν εμφανιστεί η ανάγκη


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  02-06-2005, 15:51 2408 σε απάντηση της 2407

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Η αλήθεια είναι οτι δεν...σκαμπαζα πολλά από το θέμα των merge joins (και θέλω να πάρω και το 70-229 τρομάρα μου)! Διαβασα όμως λιγάκι στα γρήγορα και το βρήκα ενδιαφέρον. Θα το κοιτάξω καλύτερα, ευχαριστώ για την χρήσιμη παρατήρηση!


    Σωτήρης Φιλιππίδης

    DotSee Web Services

    View Sotiris Filippidis's profile on LinkedIn

    DotNetNuke them!
  •  02-06-2005, 18:14 2416 σε απάντηση της 2408

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

    Εγώ θα έλεγα το έχεις:


    Όταν ένας άνθρωπος μας ανοίγει μια πόρτα μην ψάχνουμε κάθε δυνατό τρόπο για να την κλίσουμε

    Broken Heart

    while (!dead) learn();
  •  02-06-2005, 19:21 2424 σε απάντηση της 2405

    Re: Δυναμικό WHERE IN() χωρίς δυναμικη SQL

     pkanavos wrote:
    Δηλαδή, πραγματικά χρειάζεται να περάσεις τόσες τιμές?

    Indifferent

    Αποπληξία!


    Στην περίπτωση αυτή επιβάλεται το ID να είναι primary key. Ίσως μάλιστα να πρέπει να χρησιμοποιηθεί και temporary table αν οι τιμές είναι πάρα πολλές. Εδώ πλέον μιλάμε για τρελές, εξωτικές λύσεις.


    Μην αγχώνεσαι! Big Smile

    Μπορεί ο Σωτήρης να έχει πάρει το θέμα από την ελαφριά του μεριά. Ο μηχανισμός όμως αυτός, είναι ο "default" τρόπος, για να μπορέσουμε να εφαρμόσουμε security οριζόντια μέσα σε ένα πίνακα. Δεν είναι και τόσο "εξωτικός", αν μιλάμε για μεγάλη βάση.

    Δηλαδή αν δεις ένα statement που ταυτόχρονα κάνει apply οριζόντια και κάθετα security σε ένα πίνακα, τι θα πεις; Οτι θα εξολοθρεύσει κάποιος τα resources του SQL Server; Δεν είναι σχεδιασμένη η μηχανή να κάνει optimize τέτοια statements;

    Όταν ο σκοπός πρέπει να επιτευχτεί, νομίζω ότι όλα τα άλλα έρχονται σε δεύτερη μοίρα.

    George J.


    George J. Capnias: Χειροπρακτικός Υπολογιστών, Ύψιστος Γκουράρχης της Κουμπουτερολογίας
    w: capnias.org, t: @gcapnias, l: gr.linkedin.com/in/gcapnias
    dotNETZone.gr News
Σελίδα 1 από 2 (17 εγγραφές)   1 2 >
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems