Session (int)
Flusso_alimentante (varchar)
numero_riga (int)
Campo1
Campo2
Campo3
.........
Campo10
Il file (o meglio, I file, dato che sono più di uno) ha 10 campi, e questi
campi andranno copiati nei campi 1, 2,...10.
Nel campo Session ci va un default (pari a 0 quando si esegue il
caricamento), nel campo Flusso_alimentante il nome del file csv che sto
caricando, e nel campo numero_riga un numero (incrementale) legato ai record
presenti in ciascun file (primo record, secondo record, e così via).
Non riesco però a trovare una soluzione per implementare il tutto.
Qualcuno ha un'idea?
Grazie
Ciao Luigi,
non puoi effettuare questa operazione in una "botta" sola.
- Creati una tabella di appoggio avente la stessa struttura del file da
importare.
- Con una BULK INSERT popola questa tabella;
- Popola la tabella di destinazione tramite il comando INSERT...SELECT
valorizzando opportunamente le colonne mancanti: per la colonna Session
definisci un DEFAULT sulla tabella di destinazione ed escludila dalla SELECT
LIST, per Flusso_alimentante "schianta" il suo valore in una costante oppure
se hai intenzione di inglobare il tutto in una stored procedure, prevedi una
variabile di input che valorizzerai opportunamente ad ogni importazione con
il nome del file, infine per numero_riga... non ho capito come lo vuoi
valorizzare :-)
"numero (incrementale)legato ai record presenti in ciascun file (primo
record, secondo record, e così via)"
Che significa questa frase?
Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org
flusso_alimentante = Flusso1.txt
e
numero_riga che avrà i valori da 1 a 50, in base appunto al numero delle
righe che sono presenti nel file alimentante.
Tu come penseresti di fare?
"Lorenzo Benaglia" ha scritto:
CREATE PROCEDURE up_CaricaFile1
AS
BULK INSERT mioDB..IT_SYSTEM_LOAD_TEMP FROM 'c:\File1.txt'
WITH
(
FIELDTERMINATOR = ';',
ROWTERMINATOR = '|\n',
DATAFILETYPE = 'char'
)
declare @Campo1 varchar(100)
declare @Campo2 varchar(100)
declare @Campo3 varchar(100)
declare @Campo4 varchar(100)
declare @Campo5 varchar(100)
declare @Campo6 varchar(100)
declare @Campo7 varchar(100)
declare @Campo8 varchar(100)
declare @Campo9 varchar(100)
declare @Campo10 varchar(100)
select @Campo1 = Campo1 FROM IT_SYSTEM_LOAD_TEMP
/*
eccetera per tutti gli altri 9 campi
*/
-- Inserisco nella tabella finale
INSERT IT_SYSTEM_LOAD values (
0,
'File1.txt1',
'------',
@Campo1,
@Campo2,
@Campo3,
@Campo4,
@Campo5,
@Campo6,
@Campo7,
@Campo8,
@Campo9,
@Campo10)
Il problema è che non ho trovato un modo per valore il campo che ho segnato
come '------' e che dovrebbe contenere il valore incrementale della riga (da
1 fino al numero di righe che contiene il file txt).
Nella tabella di appoggio definirei una colonna IDENTITY che si
autoincrementa in modo automatico.
Il fatto è che se la tabella contiene un numero differente di colonne
rispetto al file sorgente, sarai costretto a ricorrere ad un file di formato
dove andrai ad indicare la corrispondenza tra le colonne del file di input
con quelle della tabella di destinazione.
Dato che il discorso si complica un attimino ti propongo un esempio
completo.
Supponiamo di voler importare il seguente file C:\Students.csv:
<BOF>
Lorenzo;Benaglia
Luca;Bianchi
Andrea;Montanari
Gianluca;Hotz
<EOF>
Come prima cosa vado a definire il file di formato C:\Students.fmt:
<BOF>
8.0
3
1 SQLCHAR 0 0 "" 0 numero_riga ""
2 SQLCHAR 0 10 ";" 2 FirstName SQL_Latin1_General_CP1_CI_AS
3 SQLCHAR 0 10 "\r\n" 3 LastName SQL_Latin1_General_CP1_CI_AS
<EOF>
Ora da Query Analyzer esegui il seguente script:
USE tempdb
GO
/* Definisco la tabella dbo.Students */
CREATE TABLE dbo.Students(
Session int NOT NULL DEFAULT 0,
Flusso_alimentante varchar(15),
numero_riga int NOT NULL,
FirstName varchar(10) NOT NULL,
LastName varchar(10) NOT NULL
)
GO
/* Definisco la stored procedure dbo.up_ImportStudents */
CREATE PROCEDURE dbo.up_ImportStudents(
@FileName varchar(15)
)
AS
SET NOCOUNT ON
/* Definisco la tabella di appoggio */
CREATE TABLE #TempTable(
numero_riga int NOT NULL IDENTITY,
FirstName varchar(10) NOT NULL,
LastName varchar(10) NOT NULL
)
/* La popolo con la BULK INSERT */
EXEC (
'BULK INSERT #TempTable ' +
'FROM ''' + @FileName + ''' ' +
'WITH(FORMATFILE = ''C:\Students.fmt'')'
)
SET NOCOUNT OFF
/* Popolo la tabella dbo.Students */
INSERT dbo.Students(
Flusso_alimentante
, numero_riga
, FirstName
, LastName
)
SELECT
@FileName
, numero_riga
, FirstName
, LastName
FROM #TempTable
GO
/* La eseguo */
EXEC dbo.up_ImportStudents 'C:\Students.csv'
/* Vediamo */
SELECT *
FROM dbo.Students
GO
/* Output:
Session Flusso_alimentante numero_riga FirstName LastName
----------- ------------------ ----------- ---------- ----------
0 C:\Students.csv 1 Lorenzo Benaglia
0 C:\Students.csv 2 Luca Bianchi
0 C:\Students.csv 3 Andrea Montanari
0 C:\Students.csv 4 Gianluca Hotz
(4 row(s) affected)
*/
/* Pulizia */
DROP PROCEDURE dbo.up_ImportStudents
DROP TABLE dbo.Students
Approfondisci il discorso leggendo i seguenti paragrafi dei Books Online:
"Using Format Files"
http://msdn.microsoft.com/library/en-us/adminsql/ad_impt_bcp_9yat.asp
"Using a Data File with Fewer Fields"
http://msdn.microsoft.com/library/en-us/adminsql/ad_impt_bcp_57qd.asp
valore1;valore2;;;valore5;valore6;;;;valore10
Ho quindi tutti e 10 i campi, anche se ne sono valorizzati solo 5.
"Lorenzo Benaglia" ha scritto:
No, ma se segui il mio esempio la tabella di appoggio avrà la colonna
identity numero_riga che tu non hai nei file csv che vuoi importare...
"Lorenzo Benaglia" ha scritto:
Esatto. La BULK INSERT darebbe errore. Fai prima a provare che a chiedere...
> Se utilizzo la Bulk Insert che mi inserisce tutti i campi tranne il
> primo, che è di tipo Identity, non funzionerebbe?
Oh mamma, se usi un file di formato funziona, altrimenti se il numero di
colonne del file da importare differisce da quello della tabella di
destinazione otterrai un errore.
E' tutto spiegato nei paragrafi dei BOL che ti ho segnalato!!
"Lorenzo Benaglia" ha scritto:
<BOF>
8.0
34
1 SQLCHAR 0 0 "" 0 row_number ""
2 SQLCHAR 0 100 ";" 2 system_name SQL_Latin1_General_CP1_CI_AS
3 SQLCHAR 0 100 ";"
3 system_short-description SQL_Latin1_General_CP1_CI_AS
4 SQLCHAR 0 100 ";" 4 application SQL_Latin1_General_CP1_CI_AS
5 SQLCHAR 0 100 ";" 5 server_name SQL_Latin1_General_CP1_CI_AS
6 SQLCHAR 0 100 ";" 6 operating_system SQL_Latin1_General_CP1_CI_AS
7 SQLCHAR 0 100 ";" 7 server_hw SQL_Latin1_General_CP1_CI_AS
8 SQLCHAR 0 100 ";" 8 location SQL_Latin1_General_CP1_CI_AS
9 SQLCHAR 0 100 ";" 9 engine_middleware SQL_Latin1_General_CP1_CI_AS
10 SQLCHAR 0 100 ";" 10 database SQL_Latin1_General_CP1_CI_AS
11 SQLCHAR 0 100 ";" 11 business_owner SQL_Latin1_General_CP1_CI_AS
12 SQLCHAR 0 100 ";" 12 technical_owner SQL_Latin1_General_CP1_CI_AS
13 SQLCHAR 0 100 ";" 13 opco SQL_Latin1_General_CP1_CI_AS
14 SQLCHAR 0 100 ";"
14 sox_business_processes_flag SQL_Latin1_General_CP1_CI_AS
15 SQLCHAR 0 100 ";" 15 sox_relevant SQL_Latin1_General_CP1_CI_AS
16 SQLCHAR 0 100 ";" 16 sox_relevant_reason SQL_Latin1_General_CP1_CI_AS
17 SQLCHAR 0 100 ";" 17 type SQL_Latin1_General_CP1_CI_AS
18 SQLCHAR 0 100 ";" 18 gita_domain_code SQL_Latin1_General_CP1_CI_AS
19 SQLCHAR 0 100 ";" 19 gita_subdomain-code SQL_Latin1_General_CP1_CI_AS
20 SQLCHAR 0 100 ";"
20 gita_domain_description SQL_Latin1_General_CP1_CI_AS
21 SQLCHAR 0 100 ";"
21 gita_subdomain_description SQL_Latin1_General_CP1_CI_AS
22 SQLCHAR 0 100 ";" 22 component SQL_Latin1_General_CP1_CI_AS
23 SQLCHAR 0 100 ";"
23 sox_relevant_flag_for_component SQL_Latin1_General_CP1_CI_AS
24 SQLCHAR 0 100 ";" 24 outsourced_flag SQL_Latin1_General_CP1_CI_AS
25 SQLCHAR 0 100 ";" 25 outsourcing_company SQL_Latin1_General_CP1_CI_AS
26 SQLCHAR 0 100 ";"
26 business_process_description SQL_Latin1_General_CP1_CI_AS
27 SQLCHAR 0 100 ";" 27 privacy_information SQL_Latin1_General_CP1_CI_AS
28 SQLCHAR 0 100 ";"
28 confidentiality_information SQL_Latin1_General_CP1_CI_AS
29 SQLCHAR 0 100 ";" 29 rto_target SQL_Latin1_General_CP1_CI_AS
30 SQLCHAR 0 100 ";" 30 rto_actual SQL_Latin1_General_CP1_CI_AS
31 SQLCHAR 0 100 ";" 31 rpo_target SQL_Latin1_General_CP1_CI_AS
32 SQLCHAR 0 100 ";" 32 rpo_actual SQL_Latin1_General_CP1_CI_AS
33 SQLCHAR 0 100 ";" 33 sla_category SQL_Latin1_General_CP1_CI_AS
34
SQLCHAR 0 100 "\r\n" 34 privacy_data_responsible SQL_Latin1_General_CP1_CI_AS
<EOF>
Non ho capito però dai BOL se posso mettere 0 (zero) come "Lunghezza del
prefisso". Nel tuo esempio hai messo 0 a tutti i campi, ma dice che se un
campo può essere NULL (ed è il mio caso), ci vorrebbe un altro valore.
Tu cosa ne dici?
Grazie
Ciao Luigi,
il prefisso è richiesto solo in fase di esportazione quando le colonne di
una tabella accettano il valore NULL.
I Books Online nel paragrafo "Prefix Length"
http://msdn.microsoft.com/library/en-us/adminsql/ad_impt_bcp_797s.asp
riportano:
"Null values are represented as an empty field when copied from an instance
of SQL Server to a data file. To indicate that the field is empty
(represents NULL), the field prefix contains the value -1. Any SQL Server
column that allows null values requires a prefix length of 1 or greater,
depending on the file storage type.
Use these prefix lengths when bulk copying data from an instance of SQL
Server to a data file, storing the data using either native data types or as
ASCII characters (text file)".
Ti allego un nuovo esempio dove rispetto al precedente ho eliminato il nome
di 2 studenti e ho definito la colonna FirstName in modo che accetti il
valore NULL:
C:\Students.csv:
---------------
<BOF>
Lorenzo;Benaglia
;Bianchi
Andrea;Montanari
;Hotz
<EOF>
C:\Students.fmt:
---------------
<BOF>
8.0
3
1 SQLCHAR 0 0 "" 0 numero_riga ""
2 SQLCHAR 0 10 ";" 2 FirstName SQL_Latin1_General_CP1_CI_AS
3 SQLCHAR 0 10 "\r\n" 3 LastName SQL_Latin1_General_CP1_CI_AS
<EOF>
USE tempdb
GO
/* Definisco la tabella dbo.Students */
CREATE TABLE dbo.Students(
Session int NOT NULL DEFAULT 0,
Flusso_alimentante varchar(15),
numero_riga int NOT NULL,
FirstName varchar(10) NULL,
LastName varchar(10) NOT NULL
)
GO
/* Definisco la stored procedure dbo.up_ImportStudents */
CREATE PROCEDURE dbo.up_ImportStudents(
@FileName varchar(15)
)
AS
SET NOCOUNT ON
/* Definisco la tabella di appoggio */
CREATE TABLE #TempTable(
numero_riga int NOT NULL IDENTITY,
FirstName varchar(10) NULL,
/* Output:
0 C:\Students.csv 2 NULL Bianchi
0 C:\Students.csv 3 Andrea Montanari
0 C:\Students.csv 4 NULL Hotz
(4 row(s) affected)
*/
/* Pulizia */
DROP PROCEDURE dbo.up_ImportStudents
DROP TABLE dbo.Students
Come vedi il file di formato è sempre quello e SQL Server ha interpretato
correttamente la mancanza del nome nel file di origine come valore NULL
nella tabella di destinazione.
> Grazie
Prego.
Ciao!
CREATE PROCEDURE dbo.up_ImportITSystem (
@FileName varchar(30)
)
AS
SET NOCOUNT ON
/* Definisco la tabella di appoggio */
CREATE TABLE #TempItSystemLoad
(
[row_number] [int] IDENTITY (1, 1) NOT NULL,
[system_name] [varchar] (100) NULL,
[system_short_description] [varchar] (100) NULL,
[application] [varchar] (100) NULL,
[server_name] [varchar] (100) NULL,
[operating_system] [varchar] (100) NULL,
[server_hw] [varchar] (100) NULL,
[location] [varchar] (100) NULL,
[engine_middleware] [varchar] (100) NULL ,
[database] [varchar] (100) NULL,
[business_owner] [varchar] (100) NULL ,
[technical_owner] [varchar] (100) NULL,
[opco] [varchar] (100) NULL,
[sox_business_processes_flag] [varchar] (100) NULL,
[sox_relevant] [varchar] (100) NULL,
[sox_relevant_reason] [varchar] (100) NULL,
[type] [varchar] (100) NULL,
[gita_domain_code] [varchar] (100) NULL,
[gita_subdomain_code] [varchar] (100) NULL,
[gita_domain_description] [varchar] (100) NULL,
[gita_subdomain_description] [varchar] (100) NULL,
[component] [varchar] (100) NULL,
[sox_relevant_flag_for_component] [varchar] (100) NULL,
[outsourced_flag] [varchar] (100) NULL,
[outsourcing_company] [varchar] (100) NULL,
[business_process_code] [varchar] (100) NULL,
[business_process_description] [varchar] (100) NULL,
[privacy_information] [varchar] (100) NULL,
[confidentiality_information] [varchar] (100) NULL,
[rto_target] [varchar] (100) NULL,
[rto_actual] [varchar] (100) NULL,
[rpo_target] [varchar] (100) NULL,
[rpo_actual] [varchar] (100) NULL,
[sla_category] [varchar] (100) NULL,
[privacy_data_responsible] [varchar] (100) NULL
)
/* La popolo con la BULK INSERT */
EXEC (
'BULK INSERT #TempITSystemLoad' +
'FROM ''' + @FileName + ''' ' +
'WITH(FORMATFILE = ''C:\it_system_load.fmt'')'
)
SET NOCOUNT OFF
/* Popolo la tabella sarbox.IT_SYSTEM_LOAD */
INSERT IT_SYSTEM_LOAD(
feeding_flow,
row_number,
system_name,
system_short_description,
application,
server_name,
operating_system ,
server_hw,
location,
engine_middleware,
[database],
business_owner,
technical_owner,
opco,
sox_business_processes_flag ,
sox_relevant,
sox_relevant_reason,
type,
gita_domain_code,
gita_subdomain_code,
gita_domain_description,
gita_subdomain_description,
component,
sox_relevant_flag_for_component,
outsourced_flag,
outsourcing_company,
business_process_code,
business_process_description,
privacy_information,
confidentiality_information,
rto_target,
rto_actual,
rpo_target,
rpo_actual,
sla_category,
privacy_data_responsible
)
SELECT
@FileName,
row_number,
system_name,
system_short_description,
application,
server_name,
operating_system ,
server_hw,
location,
engine_middleware,
[database],
business_owner,
technical_owner,
opco,
sox_business_processes_flag,
sox_relevant,
sox_relevant_reason,
type,
gita_domain_code,
gita_subdomain_code,
gita_domain_description,
gita_subdomain_description,
component,
sox_relevant_flag_for_component,
outsourced_flag,
outsourcing_company,
business_process_code,
business_process_description,
privacy_information,
confidentiality_information,
rto_target,
rto_actual,
rpo_target,
rpo_actual,
sla_category,
privacy_data_responsible
FROM #TempItSystemLoad
GO
EXEC dbo.up_Import_ITSystem 'C:\FileA.csv'
mi da' il seguente errore:
Server: messaggio 170, livello 15, stato 1, riga 1
Riga 1: sintassi non corretta in prossimità di 'C:\FileA.csv'.
(righe interessate: 0)
malgrado il proprietario sia corretto, e i file siano nelle giuste posizioni
"Lorenzo Benaglia" ha scritto:
BULK INSERT TempItSystemLoad FROM 'c:\FileA.csv'
WITH (FORMATFILE = 'c:\it_system_load.fmt')
mi da' il seguente errore:
Server: messaggio 4820, livello 16, stato 1, riga 1
Impossibile eseguire l'inserimento di massa. La versione del file di formato
'c:\it_system_load.fmt' è sconosciuta
Non capisco come mai.
Il file di formato è:
<BOF>
8.0
35
26 SQLCHAR 0 100 ";" 26 business_process_code SQL_Latin1_General_CP1_CI_AS
27 SQLCHAR 0 100 ";"
27 business_process_description SQL_Latin1_General_CP1_CI_AS
28 SQLCHAR 0 100 ";" 28 privacy_information SQL_Latin1_General_CP1_CI_AS
29 SQLCHAR 0 100 ";"
29 confidentiality_information SQL_Latin1_General_CP1_CI_AS
30 SQLCHAR 0 100 ";" 30 rto_target SQL_Latin1_General_CP1_CI_AS
31 SQLCHAR 0 100 ";" 31 rto_actual SQL_Latin1_General_CP1_CI_AS
32 SQLCHAR 0 100 ";" 32 rpo_target SQL_Latin1_General_CP1_CI_AS
33 SQLCHAR 0 100 ";" 33 rpo_actual SQL_Latin1_General_CP1_CI_AS
34 SQLCHAR 0 100 ";" 34 sla_category SQL_Latin1_General_CP1_CI_AS
35
SQLCHAR 0 100 "\r\n" 35 privacy_data_responsible SQL_Latin1_General_CP1_CI_AS
<EOF>
Mi rimane un mistero come mai dice che la versione è sconosciuta.
Il paragrafo "Using Format Files"
http://msdn.microsoft.com/library/en-us/adminsql/ad_impt_bcp_9yat.asp che ti
ho segnalato in precedenza riporta:
"The format file is a tab-delimited text file with a specific structure."
Il tuo format file rispecchia quella struttura?
Beh, SQL Server ha sempre ragione :-)
Ricontrolla con più calma e troverai l'errore.
Come prima cosa dovrei capire cosa vuoi fare.
Facciamo così, se mi prepari un esempietto bello chiaro come quelli che ti
ho fornito io vedrò di darti una mano, OK?
> Grazie
Prego.
Ciao!
CREATE TABLE [dbo].[NETWORK_ELEMENT_LOAD] (
[session] [int] NOT NULL ,
[feeding_flow] [varchar] (100) NOT NULL ,
[row_number] [int] NOT NULL ,
[network_element_name] [varchar] (100) NULL ,
[type] [varchar] (100) NULL
) ON [PRIMARY]
GO
Come forse ti ricordi, nel campo feeding_flow c'è il nome del file che
alimenta, nel campo session c'è il valore 0 (zero), e row_number è un
incrementale.
Una volta caricata questa tabella (metti ad esempio con 5 record):
[Tabella di alimentazione]
[session] - [feeding_flow] - [row_number] - [network_element_name] - [type]
0 c:\F6.csv 1 network_element_name1 type1
0 c:\F6.csv 2 network_element_name2 type2
0 c:\F6.csv 3 network_element_name3 type3
0 c:\F6.csv 4 network_element_name4 type4
0 c:\F6.csv 5 network_element_name5 type5
Da questa tabella devo caricarne una di "storico", che deve mantenere gli
stessi dati, tranne che la Session deve valere 1,2,3,4.....aumentando di uno
ogni volta che riporto questi 5 record nella tabella di storico.
Quindi, con la Bulk Insert ho caricato i 5 reccord soprariportati, li devo
mettere ora nella tabella di storico in modo che appaiano così fatti.
[Tabella di storico]
[session] - [feeding_flow] - [row_number] - [network_element_name] - [type]
1 c:\F6.csv 1 network_element_name1 type1
1 c:\F6.csv 2 network_element_name2 type2
1 c:\F6.csv 3 network_element_name3 type3
1 c:\F6.csv 4 network_element_name4 type4
1 c:\F6.csv 5 network_element_name5 type5
Come vedi l'unica diversità è il campo Session, ora vale 1.
Svuoto quindi la tabella di caricamento, la ripopolo con un altro successivo
file alimentante (e avrà sempre Session = 0 nella tabella alimentata), e
riporto tutto nello storico, questa volta con session però pari a 2:
[Tabella di storico]
[session] - [feeding_flow] - [row_number] - [network_element_name] - [type]
2 c:\F6.csv 1 network_element_name11 type11
2 c:\F6.csv 2 network_element_name21 type21
2 c:\F6.csv 3 network_element_name31 type31
2 c:\F6.csv 4 network_element_name41 type41
2 c:\F6.csv 5 network_element_name51 type51
Come vedi il problema è che non è possibile settare il campo Session con un
incrementale normale, perchè questo incrementale dovrebbe funzionare, come
dire, a gruppi di record, invece che con uno solo.
Tu come faresti una cosa del genere? (sperando che io sia stato abbastanza
chiaro nello spiegarti il tutto).
Ti ringrazio
Prima di popolare la tabella di storico ti calcoli il massimo valore di
Session utilizzando la funzione di aggregazione MAX(), lo incrementi di 1 e
lo assegni ad una variabile locale. Questa variabile la utilizzerai nella
INSERT finale per popolare la tabella di storico con i dati della tabella di
appoggio.