in

DotNetSide

Dot Net South Italy Developers User Group

Tips

Sql

  • Impedire avvio istanza MS SQL Server

    Autore: Francesco Quaratino

    Ecco alcune funzionalità poco note di MS-Sql Server, per la realizzazione di un'applicazione in grado di impedire a un'istanza di MS-Sql Server di avviarsi.

    Premetto che sarà bene provare il codice che presento di seguito, SOLTANTO in ambienti molto poco critici e MAI (!!!) in ambienti di produzione. Non deve essere bello, infatti, vedere che Sql Server non rispondere ai nostri ripetuti "inviti" ad avviarsi.

    Apriamo una connessione al nostro server sql, con credenziali di amministratore (per intenderci l'utente SA va benissimo), entrando nel contesto del database master.

     

    --Transact SQL
    USE master
    GO

    Iniziamo col creare una stored procedure di sistema, cioè che risiederà nel database master. Questa procedura avrà l'ingrato compito di arrestare il servizio SQL Server (mssqlserver è il servizio di default, mssql$istanzanominata in caso di istanza con nome) richiamando la stored procedure estesa xp_cmdShell.

    Per crearla come stored procedure di sistema, basta denominarla con il prefisso sp_.

     

    --Transact SQL
    CREATE PROC sp_StopService
    AS
    EXEC xp_cmdshell 'net stop mssqlserver', NO_OUTPUT
    GO

    A questo punto, la rendiamo una "autostart procedure". In tal modo, la nostra procedura sarà eseguita automaticamente subito dopo la partenza del servizio Sql, producendo lo spiacevole effetto di arrestare lo stesso servizio.

     

    --Transact SQL
    sp_procoption sp_StopService, 'startup', 'true'

    A questo punto, possiamo arrestare il servizio manualmente o, se preferite un po' più di suspense, riavviare il computer. Se non ci sono stati intoppi, il servizio Sql non si avvierà mai più perchè arrestato dalla nostra procedura auto-partente subito dopo l'avvio.

    Per disattivare la proprietà auto-partente della sp_StopService, è necessario avviare Sql Server in modalità applicazione mediante SQLSERVR.EXE usando il il trace flag 4022. Quindi, apriamo un prompt dei comandi e dopo esserci spostati nella directory che ospita SQLSERVR.EXE, digitiamo il comando dal prompt dei comandi

    (se istanza nominata, in questo esempio Sql2005 è il nome dell'istanza)

    sqlservr -sSql2005 -T4022

    (se istanza di default)

    sqlservr -T4022

    Quindi, per disattivare il comportamento auto-partente della nostra procedura, apriamo una connessione a Sql mediante credenziali amministrative entrando nel contesto del database master, e lanciamo:

     

    --Transact SQL
    sp_procoption sp_StopService, 'startup', 'false'

    Solo adesso, saremo in grado di avviare il servizio Sql (ma prima bisogna terminare l'esecuzione di sqlservr chiudendo la finestra DOS o premendo CTRL+BREAK). Lo scherzo è bello quando dura poco :)

    Posted Oct 12 2006, 08:00 AM by VitoA with 1 comment(s)
    Filed under:
  • Logiche di business mediante trigger

    Autore: Francesco Quaratino

    I trigger sono, talvolta, l'estremo rimedio a problemi altrimenti insormontabili. Anche se Microsoft tende a scoraggiarne l'uso, in alcuni casi è bene prenderli in seria  considerazione, come, per esempio, l'implementazione di logiche aziendali complesse che l'integrità referenziale non è in grado di supportare.

    Questo tips presenta un caso concreto in cui l'uso dei trigger favorisce le performance di un'applicazione. Mi riferisco a un'applicazione gestionale abbastanza comune, legata alla gestione acquisti di un prodotto: il calcolo dell'ultimo costo d'acquisto del prodotto.

    Conoscere il costo ultimo di un prodotto è di fondamentale importanza per riordinarlo dal fornitore più conveniente, così come per fissarne un prezzo di vendita.

    Spesso i progettisti software decidono di far calcolare il costo ultimo nel momento stesso in cui viene richiesto dall'utente, motivando tale scelta col fatto che si tratta di un “campo calcolabile”, il quale non necessità di essere ospitato permanentemente nel database (e di conseguenza trattato in fase di inserimento/modifica/cancellazione).

    Se, però, questa decisione non è supportata da un'analisi preventiva del carico di lavoro a cui sarà soggetta l'applicazione, la procedura che si occupa di calcolare il costo ultimo, rischia di diventare il collo di bottiglia di funzionalità vitali della nostra applicazione (come, per esempio, la fase di riordino dei prodotti la cui giacenza va sotto la soglia della scorta minima  prestabilita, che in alcune realtà commerciali è un'operazione svolta giornalmente e molto onerosa da un punto di vista delle risorse di sistema impegnate).

    Supponiamo quindi di trovarci di fronte al seguente database, che raccoglie gli ordini a fornitore di prodotti di natura non specificata:





    Per l'esattezza, ecco i significati delle tabelle:

    ●    [OrderHeader]: i dati generali degli ordini a fornitore
    ●    [OrderDetail]: i dati dettagliati dei prodotti ordinati
    ●    [Fornitore]: i dati dei fornitori che ci riforniscono i nostri depositi
    ●    [Prodotto]: i dati dei prodotti ordinabili ai ns fornitori

    Con questi dati a disposizione, ricavare - mediante Transact-SQL - il prezzo ultimo del prodotto per un particolare fornitore, significa scrivere questo codice:

    1    DECLARE 
    2 @codice_prodotto INT,
    3 @codice_fornitore INT
    4
    5 SET
    @codice_fornitore = 1
    6 SET @codice_prodotto = 3
    7
    8
    9 SELECT TOP 1
    10 prezzo_acquisto
    11 FROM
    12 OrderHeader H
    13 INNER JOIN
    14 OrderDetail D
    15 ON
    16 D.idHeader = H.id
    17 WHERE
    18 codice_prodotto = @codice_prodotto
    19 AND
    20 codice_fornitore = @codice_fornitore
    21 ORDER BY
    22 data_ordine
    23 DESC


    Questa query risolve il problema restituendo il dato desiderato, ma in presenza di migliaia di ordini tale risultato potrebbe giungere in tempi troppo lunghi per la pazienza di utente. Proviamo a risolvere l'eventuale problema di performance “materializzando” il campo calcolabile del prezzo ultimo, attraverso la tabella [CostoUltimoProdottoFornitore]:





    Quindi gestiamo la nuova tabella attraverso il seguente trigger creato sulla tabella [OrderDetail] che scatta in fase di inserimento e modifica:

    1    CREATE TRIGGER tr_insert_update_costo_ultimo
    2 ON dbo.OrderDetail
    3 AFTER
    4 INSERT, UPDATE
    5 AS
    6
    7 IF
    @@ROWCOUNT = 0
    8 RETURN
    9
    10 /* verifico l'esistenza nella tabella [CostoUltimoProdottoFornitore] della riga
    11 relativa al prodotto inserito o aggiornato nell'ordine */

    12 IF EXISTS
    13 (
    14 SELECT *
    15 FROM CostoUltimoProdottoFornitore C
    16 INNER JOIN Inserted i
    17 ON C.codice_prodotto = i.codice_prodotto
    18 INNER JOIN OrderHeader H
    19 ON H.id = i.idHeader
    20 AND H.codice_fornitore = C.codice_fornitore
    21 )
    22 /* ne aggiorno il costo ultimo */
    23 UPDATE C
    24 SET costo_ultimo = i.prezzo_acquisto
    25 FROM CostoUltimoProdottoFornitore C
    26 INNER JOIN Inserted i
    27 ON C.codice_prodotto = I.codice_prodotto
    28 INNER JOIN OrderHeader H
    29 ON H.id = i.idHeader
    30 AND H.codice_fornitore = C.codice_fornitore
    31 ELSE
    32 /* inserisco la riga corrispondete */
    33 INSERT INTO CostoUltimoProdottoFornitore
    34 ( codice_prodotto, codice_fornitore, costo_ultimo)
    35 SELECT i.codice_prodotto, H.codice_fornitore, i.prezzo_acquisto
    36 FROM Inserted i
    37 INNER JOIN OrderHeader H
    38 ON H.id = i.idHeader
    39
    40 RETURN
    41 GO


    In realtà, la gestione della tabella  [CostoUltimoProdottoFornitore] non si esaurisce con questo trigger. In genere, infatti, occorrerebbe trattare l'eventuale cancellazione del prodotto dall'ordine, così come la modifica del fornitore dell'ordine e tutte gli altri eventi che le applicazioni costruite sul database sono in grado di attivare. Potrebbe essere opportuno anche strutturare diversamente la tabella in modo da conservare un numero ragionevole di costi ultimi e non uno soltanto come nel nostro esempio, la cui complessità è stata volutamente mantenuta ridotta.

    N.B.: scarica lo script allegato per generare il database e il trigger di esempio.

     
     
      


     
    Posted Jul 12 2006, 08:57 AM by VitoA with no comments
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems