dicembre 2004 - Posts

Mi è successo di incappare in un errore riportato da SQL Server che - a mio modesto parere - se non è un buco, è quantomeno poco chiaro e rischia di trarre in inganno il DBA.
L'errore si presenta nelle situazioni in cui si definiscono diverse relazioni di integrità referenziale residenti sulla stessa tabella ed aventi come "padre" una seconda tabella.
I books on line parlano di una situazione analoga a quella che vado a descrivere solo nel caso di relazioni autoreferenzianti. In quel caso è vero che la situazione può prendere strade che portano ad un loop, ma in questo caso no!

Ma vediamo di capire bene di cosa stò parlando con un esempio... 

Supponiamo di avere due tabelle (Ordini ed Utenti) aventi la struttura riportata di seguito:

use [Test_DB]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Ordini]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Ordini]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Utenti]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Utenti]
GO


CREATE TABLE [dbo].[Ordini] (
[OrdineID] [int] NOT NULL ,
[Data] [datetime] NULL ,
[UtenteInserimento] [int] NULL ,
[UtenteAggiornamento] [int] NULL 
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Utenti] (
[UtenteID] [int] NOT NULL ,
[Cognome] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[Nome] [varchar] (50) COLLATE Latin1_General_CI_AS NULL 
) ON [PRIMARY]
GO

Una volta definite le tabelle, passiamo ad inserire le loro chiavi primarie.

ALTER TABLE [dbo].[Ordini] WITH NOCHECK ADD 
CONSTRAINT [PK_Ordini] PRIMARY KEY CLUSTERED 
(
[OrdineID]
) ON [PRIMARY] 
GO

ALTER TABLE [dbo].[Utenti] WITH NOCHECK ADD 
CONSTRAINT [PK_Utenti] PRIMARY KEY CLUSTERED 
(
[UtenteID]
) ON [PRIMARY] 
GO

A questo punto, diciamo che vogliamo porre qualche vincolo  allo scopo di garantire l'integrità dei dati presenti nelle due tabelle. La cosa più semplice e lineare sarebbe quella di inserire due foreign key per i campi UtenteInserimento ed UtenteAggiornamento vincolandoli al campo UtenteID della relativa tabella.
Se però vogliamo dare  ai nostri utenti la possibilità di modificare il codice identificativo degli utenti anche dopo che essi hanno inserito e/o aggiornato degli ordini, la definizione delle relazioni dovrà essere come riportato di seguito:

ALTER TABLE [dbo].[Ordini] ADD 
CONSTRAINT [FK_Ordini_Utenti_Inserimento] FOREIGN KEY 
(
[UtenteInserimento]
) REFERENCES [dbo].[Utenti] (
[UtenteID]
) ON DELETE CASCADE ON UPDATE CASCADE 
GO

ALTER TABLE [dbo].[Ordini] ADD 
CONSTRAINT [FK_Ordini_Utenti_Aggiornamento] FOREIGN KEY 
(
[UtenteAggiornamento]
) REFERENCES [dbo].[Utenti] (
[UtenteID]
) ON DELETE CASCADE ON UPDATE CASCADE 
GO

Specificando le clausole ON DELETE e ON UPDATE, infatti, si ottiene la propagazione automatica su tutti i record referenziati delle eventuali modifiche e cancellazioni svolte sulla tabella  Utenti.

Se, però, si esegue quest'ultima parte dello script si ottiene il seguente errore:

Server: messaggio 1785, livello 16, stato 1, riga 1
L'impostazione del vincolo FOREIGN KEY 'FK_Ordini_Utenti_Aggiornamento' nella tabella 'Ordini' potrebbe generare cicli o percorsi a catena multipla. Specificare ON DELETE NO ACTION oppure ON UPDATE NO ACTION oppure modificare altri vincoli FOREIGN KEY.
Server: messaggio 1750, livello 16, stato 1, riga 1
Impossibile creare il vincolo. Vedere gli errori precedenti.

Analizzando la struttura (molto semplice, per la verità) delle due tabelle, è evidente che non c'è alcun rischio di ciclo o di percorso multiplo come invece ci segnala l'RDBMS di Microsoft.

Soluzione
Non esiste una soluzione "pulita": SQL Server 2000 non supporta questo tipo di integrità referenziale "multipla". Faccio notare, però, che RDBMS come Oracle o addirittura Access la supportano tranquillamente...
L'unica scappatoia che si presenta è quella di implmentare l'integrità referenziale non con le relazioni bensì con i trigger.

Lorenzo Braidi
...acquista "Database Design", il mio primo libro!

Filed under: ,
Prosegue anche questo mese il corso di Database Design che stò tenendo su DEV nella rubrica "Programmo subito". Questa lezione è interamente dedicata alle viste: cosa sono, a cosa servono, come si definiscono, ecc.


Nei mesi scorsi (a Marzo) è uscito su Visual Basic Journal un mio articolo che illustrava l'algoritmo che si utilizza per calcolare e validare un codice fiscale. Allegato a quell'articolo c'era anche un piccolo progettino VB6 che implementava l'algoritmo oggetto dell'articolo. Un lettore si è accorto di un caso particolare nel quale il software suddetto non funzionava correttamente. Nel numero di VBJ di questo mese è pubblicata la correzione di tale baco. Comunque, il software che si scarica dal sito FTP di Infomedia è stato immediatamente corretto...
Filed under: ,
In diverse occasioni mi è capitato di incappare in un errore dell'Agent di SQL Server 2000 al quale, fino ad ora, non avevo mai trovato soluzione.

L'errore si verifica quando si eseguono Job aventi più di uno step. C'è da dire che il malfunzionamento, però, si verifica solo se si utilizza la versione italiana di SQL Server 2000 con applicato il service pack 3a (SP3a). In queste condizioni si ottiene la chiusura del servizio e si ritrova nell'event log la seguente segnalazione

Exception 5 caught at line 191 of file ..\src\refreshr.c. SQLServerAgent initiating self-termination.

Leggendo un po' in giro, era abbastanza chiaro che l'errore fosse tipico solo della versione italiana, quindi ci ho provato... et voila!

Per risolvere la questione è abbastanza sostituire il componente che genera il problema con quello della versione inglese: sostituire cioé il file sqlagent.rll presente nella sub-directory 1040 con quello omonimo della directory 1033.

Lorenzo

with 6 comment(s)
Filed under: ,
Dopo tanta attesa (almeno da parte mia...), finalmente il mio primo libro ha visto la luce!

Rispetto ad una versione che avevo pubblicato su questo stesso blog, la copertina ha subito qualche piccola variazione, ma la sostanza non cambia.

Potete trovare tutte le informazioni relative al libro qui

Scarica il PDF del sommario dettagliato!

Qualche nota tecnica:

Pagine: 400 circa

Formato: 17 x 24

ISBN: 88-481-1694-9

Prezzo: solo :-) € 29,90

Buona lettura

Filed under:

E' da pochi giorni disponibile la nuova versione di PostgreSQL: il DBMS freeware che - stando alle anticipazioni - dovrebbe essere il primo ad aderire appieno allo standard SQL:2003.  La versione che si può tranquillamente e gratuitamente scaricare dal sito FTP Italiano è, in realtà, la Release Candidate 1 (Versione 8.0.0 RC1). La versione definitiva è prevista per la seconda metà del mese... in ogni caso, prima di Natale.

Lorenzo

Filed under:

Un lettore mi ha segnalato un sito nel quale è possibile trovare gratuitamente una certa mole di documenti riguardanti Oracle. Sono di particolare interesse quelli relativi al tuning del database... Molto interessante!

Buona lettura

 

Filed under:

Ho visto su alcuni blog tecnici che il mio articolo sulla paginazione dei dati pubblicato su Computer Programming n. 140 (Novembre 2004) sta facendo discutere.
Innanzitutto riporto di seguito la schematizzazione della soluzione che propongo nell'articolo così come l'ha rielaborata Riccardo Golia nel suo blog


SELECT TOP [dimPage] [field1], [field2], ...
FROM (SELECT TOP [dimPage] [field1], [field2], ...
FROM (SELECT TOP ([dimPage] * [numPage]) [field1], [field2], ...
FROM [table]
WHERE [conditions]
ORDER BY [order] ASC) AS TMP1
ORDER BY [order] DESC) AS TMP2
ORDER BY [order] ASC

I principali dubbi che ho avuto modo di leggere riguardano in particolare la possibilità di ottenere il numero totale dei record che fanno parte del resultset che si vuole trattare. Perché si possa ottenere tale informazione è necessario eseguire a priori una select count che estragga il dato e successivamente si procederà con l'estrazione della pagina desiderata.

Faccio notare che, come detto anche nell'articolo, se non si conosce a priori il numero totale dei record coinvolri, non è possibile sfruttare in maniera ottimale la soluzione descritta.
Se per esempio abbiamo una tabella con 100 record e li vogliamo visualizzare raggruppati in pagine da 20 righe ciascuna, il risultato sarà corretto per le prime 5. Nel momento, però, in cui richiederemo la visualizzazione della sesta pagina, otterremo gli stessi venti record che comparivano nella quinta. Questo comportamento può essere evitato solo se si conosce a priori il numero totale dei record e così anche il numero di pagine che ne deriveranno.

In un commento di uno dei blog che ho letto, poi, veniva proposta una soluzione alternativa. La soluzione prevede sostanzialmente di estrarre con una query tutti i record che si vogliono "saltare" e con una seconda query di individuare quelli della pagina richiesta.

SELECT TOP 20 *
FROM Tabella
WHERE Chiave NOT IN (
    SELECT TOP 60 Chiave
    FROM Tabella
    ORDER BY Chiave))
ORDER BY Chiave

La soluzione è senz'altro corretta, ma si deve prestare attenzione al fatto che essa funziona correttamente solo se si utilizza come criterio di ordinamento la chiave della tabella o un suo equivalente.

Lorenzo


Filed under: