Newsgroup: it.comp.lang.visual-basic
Titolo: UNION di due viste

Ciao.
Ho creato due viste che estraggono alcuni dati da un po' di tabelle, tra cui
i dati anagrafici di persone / imprese.
Nella prima vista sono presenti i dati di persone fisiche, mentre nella
seconda sono presenti i dati delle imprese.
I campi mancanti nel primo risultato (per esempio RagSoc e LegRap) vengono
impostati a Null e viceversa i campi mancanti nel secondo (Cognome e Nome).
Per semplificare vi mostro le due select
SELECT Cognome, NULL AS LegRap FROM Anagrafi
UNION
SELECT NULL AS Cognome, LegRap FROM Imprese
Fin qui tutto OK.
Se però metto la prima select in una vista (vw_1) e la seconda select in
un'altra (vw_2) e tento la union tra le due cosi':
SELECT * from vw_1
UNION
SELECT * from vw_2
Ottengo l'errore:
È stato rilevato un errore di sintassi durante la conversione del valore
varchar 'Rossi' in una colonna di tipo int.
Per la cronaca Rossi è il cognome del primo record.
Sto usando Sql Server 2000.
Qualcuno mi sa spiegare perche' succede?
Grazie.
Ciao
-- 
Skysurfer

Hai provato a mettere " " al posto di NULL nei primi due campi?
Ciao, Vincenzo

 Ciao, Vincenzo
Si, ho provato e in effetti così funziona.
Ma volevo sapere come mai se uso il codice sql puro non dà lo stesso
problema che con la Union di due viste.
Ciao
-- 
Skysurfer

 problema che con la Union di due viste.
Ciao Skysurfer,
se definisci una vista basata su una SELECT NULL, ..., SQL Server assegnerà
di default il data type int a tale colonna.
I prerequisiti per l'utilizzo dell'operatore UNION sono:
- Il numero e l'ordine delle colonne deve essere identico in tutte le
  queries;
- I data types devono essere compatibili (attraverso un cast implicito).
Nel caso in cui tali condizioni siano rispettate ma le dimensioni di una
colonna differiscono dalla corrispondente in un'altra query, la colonna
risultante avrà dimensioni pari alla colonna più lunga.
Come puoi notare int e varchar (o nvarchar) sono data types incompatibili
tra di loro (pertanto richiedono un cast esplicito mediante le funzioni CAST
e CONVERT).
Come riportato nel paragrafo "Data Type Precedence"
URL:tsqlref.chm::/ts_da-db_2js5.htm, int ha una precenza maggiore rispetto a
varchar e nvarchar, quindi SQL Server cercherà di castare implicitamente a
int la colonna varchar ma dato che la stringa non contiene una informazione
numerica verrà rilanciata l'eccezione che segnali.
Esempio:
USE Northwind
GO
/* Query */
SELECT CompanyName, ContactName
FROM dbo.Customers
UNION
SELECT NULL, LastName + ' ' + FirstName
FROM dbo.Employees
GO
/* Output:
CompanyName                              ContactName
---------------------------------------- ------------------------------- 
NULL                                     Buchanan Steven
...                                      ...
NULL                                     Suyama Michael
Alfreds Futterkiste                      Maria Anders
...                                      ...
Wolski  Zajazd                           Zbyszek Piestrzeniewicz
(100 row(s) affected)
*/
/* Definisco la vista dbo.vwCustomers */
CREATE VIEW dbo.vwCustomers
AS
SELECT CompanyName, ContactName
FROM dbo.Customers
GO
/* Definisco la vista dbo.vwEmployees */
CREATE VIEW dbo.vwEmployees(
CompanyName, ContactName
)
AS
SELECT NULL, LastName + ' ' + FirstName
FROM dbo.Employees
GO
/* Query */
SELECT *
FROM dbo.vwCustomers
UNION
SELECT *
FROM dbo.vwEmployees
GO
/* Output:
Server: Msg 245, Level 16, State 1, Line 1
Syntax error converting the nvarchar value 'Alfreds Futterkiste' to a column
of data type int.
*/
/* Colonne della vista dbo.vwEmployees */
SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'vwEmployees'
GO
/* Output:
COLUMN_NAME   IS_NULLABLE DATA_TYPE
------------- ----------- -----------
CompanyName   YES         int
ContactName   No          nvarchar
(2 row(s) affected)
*/
/* Soluzione 1
** Effettuo un cast esplicito a nvarchar
*/
SELECT *
FROM dbo.vwCustomers
UNION
SELECT CAST(CompanyName AS nvarchar(40)), ContactName
FROM dbo.vwEmployees
GO
/* Soluzione 2
** Ricorro alle funzioni CASE, ISNULL() o COALESCE()
*/
SELECT *
FROM dbo.vwCustomers
UNION
SELECT
CASE
    WHEN CompanyName IS NULL THEN ''
END, ContactName
FROM dbo.vwEmployees
GO
/* Soluzione 3
** Modifico la vista dbo.vwEmployees specificando
** una stringa nulla per la colonna CompanyName
*/
ALTER VIEW dbo.vwEmployees(
CompanyName, ContactName
)
AS
SELECT '', LastName + ' ' + FirstName
FROM dbo.Employees
GO
/* Query */
SELECT *
FROM dbo.vwCustomers
UNION
SELECT *
FROM dbo.vwEmployees
GO
/* Pulizia */
DROP VIEW dbo.vwCustomers, dbo.vwEmployees
Ciao!
-- 
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://mvp.support.microsoft.com
http://italy.mvps.org

 assegnerà di default il data type int a tale colonna.
[...]
Grazie mille, sei stato molto chiaro ed esauriente.
Ciao.
-- 
Skysurfer

...

Ti do una soluzione pura e semplice al tuo problema, diciamo da
mettere nella bibbia dei tip & tricks del t-sql (in realtà non so se
qualcuno abbia mai utilizzato questa tecnica).
E' vero che SQLServer assegna di default il datatype int alle colonne
con valori Null ma si può "fregare l'amico" (come dico sempre io) in
un semplice modo, esempio:
<sql>
    Select
        "Mario"    [name]
    ,   "Rossi"    [surname]
    From
        dbo.Contacts
    Union
    Select
        NULL
    ,   NULL
    ,   *
    From
        dbo.Contacts_Addresses
</sql>
L'esecuzione di questa query rilascerebbe l'errore di impossibilità di
conversione del valore "Mario" ma puoi risolvere il problema in questo
modo:
<sql>
    Select
        "Mario"    [name]
    ,   "Rossi"    [surname]
    From
        dbo.Contacts
    Union
    Select
        Convert(varChar(32),NULL)
    ,   Convert(varChar(32),NULL)
    ,   *
    From
        dbo.Contacts_Addresses
</sql>
Inserendo la funzione di conversione del tipo di dato, verrà assegnato
al tipo del campo il tipo specificato all'interno della funzione
Convert (con cast non l'ho mai provato). Vale anche (e soprattutto)
per le viste.
Spero di esserti stato d'aiuto,
+--+
|JP|
+--+

 qualcuno abbia mai utilizzato questa tecnica).
<SNIP>
Ciao JP,
Prova a rileggere la Soluzione 1 :-P
/* Soluzione 1
** Effettuo un cast esplicito a nvarchar
*/
SELECT *
FROM dbo.vwCustomers
UNION
SELECT CAST(CompanyName AS nvarchar(40)), ContactName
FROM dbo.vwEmployees
GO
Ciao!


JohnPlayerSpecial wrote:
[...]

Grazie.
Avevo già modificato la vista per farmi tornare stringhe vuote invece di
Null, ma prederò in considerazione anche il tuo suggerimento.
Grazie e ciao.
-- 
Skysurfer

...

Ciao,
scusate ho sbagliato l'esempio. Quello corretto sarebbe questo che va
bene sia per l'unione di due Select su tabelle che su viste.
In questo caso aggiungerò anche la clasola FOR XML alla fine.
<tsql>
     Create View dbo.vw_Contacts_forxml As
     Select
         1                          [Tag]
     ,   Null                       [Parent]
     ,   c.ContactId                [Contact!1!ContactId]   --// Int
     ,   c.Name                     [Contact!1!Name]        --//
VarChar(32)
     ,   c.Surname                  [Contact!1!Surname]     --//
VarChar(32)
     ,   Convert(int,NULL)          [Address!2!ContactId]   --// Int
     ,   Convert(varChar(64),NULL)  [Address!2!Address]     --//
VarChar(64)
     ,   Convert(varChar(64),NULL)  [Address!2!City]        --//
VarChar(64)
     From
         dbo.Contacts c
     Go
     Create View dbo.vw_Contacts_Addresses_forxml As
     Select
         2                          [Tag]
     ,   1                          [Parent]
     ,   c.ContactId                [Contact!1!ContactId]   --// Int
     ,   Convert(varChar(32),NULL)  [Contact!1!Name]        --//
VarChar(32)
     ,   Convert(varChar(32),NULL)  [Contact!1!Surname]     --//
VarChar(32)
     ,   ca.ContactId               [Address!2!ContactId]   --// Int
     ,   ca.Address                 [Address!2!Address]     --//
VarChar(64)
     ,   ca.City                    [Address!2!City]        --//
VarChar(64)
     From
         dbo.Contacts_Addresses ca Inner Join
         dbo.Contacts c ON c.ContactId = ca.ContactId
     Go
     Select * From dbo.vw_Contacts_forxml
     Union
     Select * From dbo.vw_Contacts_Addresses_forxml
     Order By
         [Contact!1!ContactId], [Address!2!ContactId]
     For Xml Explicit
</tsql>
Risultato tipo:
--------------------------------------------------
1    Mario    Rossi    NULL   NULL         NULL
1    NULL     NULL     1      Via Roma     Milano
1    NULL     NULL     1      Via Milano   Roma
2    Maria    Verdi    NULL   NULL         NULL
2    NULL     NULL     2      Via Torino   Verona
2    NULL     NULL     2      Via Verona   Torino
@sky: il tuo esempio è giusto, questo è un pò diverso e forse più
utilizzabile.
Ciao,
+--+
|JP|
+--+