Avatar billede montago Praktikant
03. august 2009 - 10:38 Der er 16 kommentarer og
1 løsning

SqlBulkCopy (Bulk insert) Charset problemer

Jeg sidder og er igang med at importere en ordentlig røvfuld records i en database, men har problemer med charset...


dataene ligger i tekst-filer a'la:

|0002|035|K11060|255|0|006|Oil:  AA1, 105 ml, PAG 100
|0002|035|K11060|255|0|007|Displacement: 79 cm³
|0002|035|K11060|255|0|008|12V
|0002|035|K11060|255|0|009|new

som man kan se, er der et 3'grads tegn (håber jeg)..
Filen er i sig selv encoded i ANSI charset

og når jeg læser filen i C#, bliver dataene også læst rigtigt og alting ser rigtigt ud...

lige indtil de ligger i databasen !

herinde bliver alle specialtegn lavet om til �

2
35
K11060
1
0
7
Hubraum: 79 cm�


2
35
K11060
4
0
7
Displacement: 79 cm�


min database kører standard "Dansk", så spørgsmålet er om jeg skal konvertere tegnene inden jeg indsætter dem - eller kan jeg lave nogle indstillinger i min connectionstring ?

what to do ?
Avatar billede aaberg Nybegynder
03. august 2009 - 10:57 #1
Hvis kolonnen i databasen er af typen VARCHAR eller CHAR, er det ikke sikkert du kan få tegnet med. Denne type bruger databasens karaktersæt. Prøv i stedet at lave datatypen om til NVARCHAR eller NCHAR, dette er en unicode datatype, den skal kunne indeholde alle tegn.
Avatar billede montago Praktikant
03. august 2009 - 11:32 #2
typen er Varchar ... men jeg kan fint selv skrive tegnene i MSSMSE...
Avatar billede aaberg Nybegynder
03. august 2009 - 11:46 #3
Kan jeg se koden som skriver til databasen?
Avatar billede montago Praktikant
05. august 2009 - 12:57 #4
koden ser nogenlunde sådan her ud:


SqlConnection conn = new SqlConnection(Program.dbConn);
conn.Open();

SqlBulkCopy bc = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, null);

bc.BatchSize = 10000;
bc.DestinationTableName = "DestTable";
bc.WriteToServer(GetDataTable(dv)); //DataTable

----------------------
GetDataTable opretter et DataTable nogenlunde sådan her :


SqlCommand comm = new SqlCommand(
    "select column_name, data_type from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '" + dv.Value.DestinationTable + "'",
    conn
);
SqlDataReader reader = comm.ExecuteReader();
DataTable dt = new DataTable();

while (reader.Read())
{
    switch (reader[1] as string)
    {
        case "int": dt.Columns.Add(reader[0] as string, typeof(int)); break;
        case "bigint": dt.Columns.Add(reader[0] as string, typeof(Int64)); break;
        case "float": dt.Columns.Add(reader[0] as string, typeof(double)); break;
        case "datetime": dt.Columns.Add(reader[0] as string, typeof(DateTime)); break;
        case "varchar": dt.Columns.Add(reader[0] as string, typeof(string)); break;
        case "nvarchar": dt.Columns.Add(reader[0] as string, typeof(string)); break;
        case "uniqueidentifier": dt.Columns.Add(reader[0] as string, typeof(Guid)); break;
        default: throw new Exception("Error in casting: " + reader[1]);
    }
}

----------------------
Hvorefter DataTable't bliver fyldt med 10.000 rækker data:

foreach(var line in lines.Take(10000))
{
  dt.Rows.Add(line.Split('|').ToItemArray(dt));
}

----------------------
funktionen ToItemArray tager et stringArray og returner et ObjectArray efter de Typer som er defineret i DataTablet:


public static object[] ToItemArray(this string[] arr, DataTable dt)
{
    object[] output = new object[arr.Length];

    for (int i = 0; i < arr.Length; i++)
    {
        switch (dt.Columns[i].DataType.ToString())
        {
            case "System.Int32":
                switch (arr[i])
                {
                    case "": output[i] = 0;
                        break;
                    default: output[i] = Convert.ToInt32(arr[i]);
                        break;
                }
                break;
            case "System.Int64":
                output[i] = Convert.ToInt64(arr[i]);
                break;
            case "System.Double":
                switch (arr[i])
                {
                    case "": output[i] = 0;
                        break;
                    default: output[i] = Convert.ToDouble(arr[i]);
                        break;
                }
                break;
            case "System.DateTime":
                if (arr[i].Length == 8)
                    output[i] = DateTime.ParseExact(arr[i], "yyyyMMdd", null);
                else
                    output[i] = DateTime.Now.AddDays(365);
                break;
            case "System.String":
                output[i] = arr[i];
                break;
            case "System.Guid":
                output[i] = new Guid(arr[i]);
                break;
            default:
                throw new Exception(dt.Columns[i].DataType.ToString());
        }
    }

    return output;
}
Avatar billede montago Praktikant
05. august 2009 - 13:00 #5
Jeg ved ikke om det er optimalt... men jeg kan indsætte omkring 2mio rækker i minuttet på denne her måde...
Avatar billede aaberg Nybegynder
05. august 2009 - 14:52 #6
Jeg kan desværre ikke lige se hvorfor det ikke virker. Det eneste bud jeg har, er stadig at det har noget med karaktersættet at gøre. Men hvis du kan copy/paste det ind, er det lidt mærkeligt!
Avatar billede Syska Mester
05. august 2009 - 17:42 #7
Jeg har haft noget ala samme problem ... varchar kan godt vise dem vist nok ... men nvarchar mener jeg at gjorde trikket for mig.

Hvordan læser du data fra filen ?
Avatar billede montago Praktikant
06. august 2009 - 09:43 #8
nej det er lidt mystisk...

Når jeg åbner datafilen i Notepad++ er encoding sat til ANSI, og tegnene ser fine ud.

dét jeg ikke forstår er hvorfor de bliver mærkelige når de bliver læst ind... :-/
Avatar billede Syska Mester
06. august 2009 - 11:41 #9
igen ... hvordan åbner du filen i dit program ... ?
Avatar billede montago Praktikant
12. august 2009 - 09:26 #10
sådan her:

StreamReader sr = new StreamReader(dv.Value.DestinationTable + ".tbl", Encoding.Default, true);

while (!sr.EndOfStream)
{
    string line = sr.ReadLine();
Avatar billede Syska Mester
12. august 2009 - 14:47 #11
Mærkeligt ... men out of the blue comments må være følgende.

hmmm, har du for sjov prøvet andre Encodings selvom det måske ser fint ud i programmet ... kan jo ske at det går galt når det skal ind i databasen ...

ASCII burde være hvad der svarer til ANSI ... efter som hvad jeg lige kunne læse mig til at ANSI er et udvidet ASCII charset måske forkert ... men det var hvad google lige gav mig.

Så Default er nok det systemet normalt bruger ... og jo ikke sikkert det er den encoding du har gemt det i hvis du specifikt siger det skal være ANSI.

// ouT
Avatar billede montago Praktikant
12. august 2009 - 15:07 #12
hey... jeg fik løst problemet...

1. StreamReader sættes til at læse med Encoding.Default + autodetect
2. Linien i filen læses med CurrentEncoding og konverteres til Unicode Byte[]
3. byte[] konverteres tilbage til String


StreamReader sr = new StreamReader(dv.Value.DestinationTable + ".tbl", Encoding.Default, true);

while (!sr.EndOfStream)
{
    string line = Encoding.Unicode.GetString(Encoding.Convert(sr.CurrentEncoding, Encoding.Unicode, sr.CurrentEncoding.GetBytes(sr.ReadLine())));
Avatar billede Syska Mester
12. august 2009 - 16:06 #13
CurrentEncoding er UTF8 som jeg fik det læst på nettet.

hvis du laver det om til Unicode ...

Er følgende så ikke nemmere eller er der noget jeg har overset:

StreamReader reader = new StreamReader(path, Encoding.Unicode, true);

????

// ouT
Avatar billede Syska Mester
12. august 2009 - 16:07 #14
Som min hentydning fra start også var at din Encoding var forkert, derfor jeg ville vide hvordan du læste din data ud ...

// ouT
Avatar billede montago Praktikant
12. august 2009 - 16:20 #15
Er følgende så ikke nemmere eller er der noget jeg har overset:
StreamReader reader = new StreamReader(path, Encoding.Unicode, true);

nej - for hvis filen er skrevet med UTF8 så skipper din StreamReader den encoding du giver den og finder selv ud af det ved at læse de første bytes af filen.

når en StreamReader læser filen, bliver streng-outputtet til Unicode - men selvom det er unicode, findes der flere placeringer af æøå hvilket kan give fejl senere hen.

Encoding.Default er Win1252 hvilket ikke er præcis det samme som ANSI eller ASCII
Avatar billede Syska Mester
12. august 2009 - 17:30 #16
Så du siger man burde sætte den til false istedet for true ...

Det virker bare ikke videre specielt elegant måde at læse det ind på og så convert det hele ... kan ik' passe det ik' kan gøre smartere ...

Men jeg har ikke de nødvendige ting til at teste det selv ... for det virker lidt som om der noget helt galt her hvis det ikke kan gøres i første hug.

well ... jeg er i hvert fald glad for at du endelig fik det til at virke :-)
Avatar billede montago Praktikant
12. august 2009 - 18:58 #17
> Så du siger man burde sætte den til false istedet for true ...

nej jeg gør ikke :)

> for det virker lidt som om der noget helt galt her hvis det ikke kan gøres i første hug.

ja det syntes jeg også... men det var sgu strengen som var fucked
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester



IT-JOB