Problema con una view in SQL Server 2000

venerdì 28 ottobre 2005 - 16.33

fornonad Profilo | Newbie

Ho esattamente un record fatto in questo modo:

NOME DATA NUM_DOMANDA PUNTI RIF_RISPOSTA DATA_COMPILAZIONE
UTENTE1 10/10/2005 44 1 138|139 10/10/2005 13.02.25

In RIF_RISPOSTA ho le due risposte selezionate nella compilazione
Vorrei ottenere una view in cui ho le due risposte separate, e quindi:

NOME DATA NUM_DOMANDA PUNTI RIF_RISPOSTA DATA_COMPILAZIONE
UTENTE1 10/10/2005 44 1 138 10/10/2005 13.02.25
UTENTE1 10/10/2005 44 1 139 10/10/2005 13.02.25

è possibile farlo in una view? mi tocca creare una tabella con un cursore?
io uso SQL Server 2000
grazie dell'aiuto

lbenaglia Profilo | Guru

>Ho esattamente un record fatto in questo modo:
>è possibile farlo in una view? mi tocca creare una tabella con
> un cursore?

Questo è quello che succede quando non si rispetta nemmeno la prima forma normale nella progettazione di un database... :-(
La 1NF dice: "In una tabella ogni colonna deve contenere una informazione atomica e non devono esistere gruppi di colonne ripetute".

Prima di abbozzare una risposta, mi sai dire la colonna Rif_Risposta quanti valori può contenere separati da una pipe "|"?
2, 3, 1 milione? LOL

>grazie dell'aiuto
Prego.

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

fornonad Profilo | Newbie

penso che al massimo possa contenere 5 valori differenti

ciao ciao adry

lbenaglia Profilo | Guru

>penso che al massimo possa contenere 5 valori differenti

Ciao adry,

se utilizzassi SQL Server 2005 potresti avvalerti dell'operatore APPLY che valuta la tabella di destra per ogni riga della tabella di sinistra. Questa funzionalità è utile quando la tabella di destra è costituita da una funzione table-valued parametrica che recupera gli argomenti dalle colonne della tabella di sinistra.

Nell'esempio che ti sto per proporre ho definito la funzione table-valued ufn_Split che restituisce un resultset di una colonna con le risposte in base al separatore specificato.
Utilizzando l'operatore CROSS APPLY effettuo una JOIN tra la tabella base e la funzione:

USE tempdb;
GO

/* Definisco la tabella dbo.Foo */
CREATE TABLE dbo.Foo(
NOME varchar(10) NOT NULL,
DATA smalldatetime NOT NULL,
NUM_DOMANDA tinyint NOT NULL,
PUNTI tinyint NOT NULL,
RIF_RISPOSTA varchar(20) NOT NULL,
DATA_COMPILAZIONE smalldatetime NOT NULL
);
GO

/* La valorizzo */
INSERT dbo.Foo VALUES('UTENTE1', '20051010', 44, 1, '138|139', '20051010 13:02:25');
INSERT dbo.Foo VALUES('UTENTE2', '20051011', 55, 2, '140', '20051011 13:02:25');
INSERT dbo.Foo VALUES('UTENTE3', '20051012', 66, 3, '141|142|143', '20051012 13:02:25');
INSERT dbo.Foo VALUES('UTENTE4', '20051013', 77, 4, '144|145|146|147|148', '20051013 13:02:25');
GO

/* Definisco la Multistatement table-valued function dbo.ufn_Split */
CREATE FUNCTION dbo.ufn_Split(
@Expression varchar(20),
@Delimiter char
)
RETURNS @Table TABLE(
Items tinyint NOT NULL
)
AS
BEGIN
DECLARE @Start int
DECLARE @End int
DECLARE @Item tinyint

SET @Start = 1

WHILE(@Start <= LEN(@Expression))
BEGIN
SET @End = CHARINDEX(@Delimiter, @Expression, @Start)
IF @End = 0 SET @End = DATALENGTH(@Expression) + 1

SET @Item = CAST(SUBSTRING(@Expression, @Start, @End - @Start) AS tinyint)

INSERT @Table VALUES(@Item)
SET @Start = @End + DATALENGTH(@Delimiter)
END
RETURN
END;
GO

/* Definisco la vista dbo.vw_NormalizeData */
CREATE VIEW dbo.vw_NormalizeData
AS
SELECT
F.NOME
, F.DATA
, F.NUM_DOMANDA
, F.PUNTI
, S.Items AS RIF_RISPOSTA
, F.DATA_COMPILAZIONE
FROM dbo.Foo AS F CROSS APPLY dbo.ufn_Split(F.RIF_RISPOSTA, '|') AS S;
GO

/* Query */
SELECT *
FROM dbo.vw_NormalizeData;
GO

/* Output:

NOME DATA NUM_DOMANDA PUNTI RIF_RISPOSTA DATA_COMPILAZIONE
---------- ----------------------- ----------- ----- ------------ -----------------------
UTENTE1 2005-10-10 00:00:00 44 1 138 2005-10-10 13:02:00
UTENTE1 2005-10-10 00:00:00 44 1 139 2005-10-10 13:02:00
UTENTE2 2005-10-11 00:00:00 55 2 140 2005-10-11 13:02:00
UTENTE3 2005-10-12 00:00:00 66 3 141 2005-10-12 13:02:00
UTENTE3 2005-10-12 00:00:00 66 3 142 2005-10-12 13:02:00
UTENTE3 2005-10-12 00:00:00 66 3 143 2005-10-12 13:02:00
UTENTE4 2005-10-13 00:00:00 77 4 144 2005-10-13 13:02:00
UTENTE4 2005-10-13 00:00:00 77 4 145 2005-10-13 13:02:00
UTENTE4 2005-10-13 00:00:00 77 4 146 2005-10-13 13:02:00
UTENTE4 2005-10-13 00:00:00 77 4 147 2005-10-13 13:02:00
UTENTE4 2005-10-13 00:00:00 77 4 148 2005-10-13 13:02:00

(11 row(s) affected)

*/

/* Pulizia */
DROP VIEW dbo.vw_NormalizeData;
DROP FUNCTION dbo.ufn_Split;
DROP TABLE dbo.Foo;


Purtroppo SQL Server 2000 non dispone dell'operatore APPLY quindi non è possibile adottare una soluzione del genere. Sinceramente non mi vengono in mente soluzioni set oriented, quindi credo che la strada del cusore possa essere

fornonad Profilo | Newbie

Ti ringrazio della risposta, purtroppo ora ho modificato la base dati lasciando la possibilità di inserire una solo possibile valore e non più valori separati da |. Comunque sarà un errore che non commetterò più.

Grazie dell'interessamento

ciao ciao adry

lbenaglia Profilo | Guru

>Ti ringrazio della risposta, purtroppo ora ho modificato la base
>dati lasciando la possibilità di inserire una solo possibile
>valore e non più valori separati da |. Comunque sarà un errore
> che non commetterò più.

Ciao adry,

hai fatto sicuramente la scelta migliore.
Partire da una basi dati normalizzata, ti eviterà in futuro di incontrare problemi analoghi a quello descritto.

>Grazie dell'interessamento
Di niente.

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5