Avatar billede Syska Mester
14. januar 2009 - 02:05 Der er 37 kommentarer og
1 løsning

Compare på 2 string giver forkert resultat.

Hej,

http://syska.dk/upload/capper-42.png

Kloge råd er dyre ... kig på overstående billede.

Ved debug kan jeg se at mine 2 stringe er ens.

Når jeg udskriver deres værdi, char for char, er de også ens.

Hvad går der galt ? Hvad har jeg overset ?

Ene string kommer fra en tekst fil ...
StreamReader sr = new StreamReader(MapPath + "\\" + FileName, System.Text.Encoding.UTF8)

Andet kommer fra en database ...

Collation i databasen er Danish_Norwegian_CI_AS

HELP, totally lost here ... noget jeg har overset ?
Avatar billede lasserasch Juniormester
14. januar 2009 - 02:24 #1
Jeg er lige med på en lytter....
Avatar billede arne_v Ekspert
14. januar 2009 - 02:36 #2
øh

så vidt jeg kan se er logikken:

if(new != current) {
  current = new
  // dump current
  // dump new
}

du skal nok dump inden du sætter current lig med new.
Avatar billede Syska Mester
14. januar 2009 - 03:02 #3
haha ... okay, første dumme fejl ...

Men det har så noget med encoding at gøre ... nu er der nemlig forskel :-p

men ... så må det jo være noget med at jeg skal convert en af mine stringe til UTF8 eller ? Hvad ligger data gemt som i en MSSQL 2008 DB ? Bliver hentet ind med LINQ to SQL datacontext.
Avatar billede arne_v Ekspert
14. januar 2009 - 03:29 #4
Strings i memory er altid Unicode UTF-16.

Så i givet fald er det indlæsningen fra fil og DB den er gal med.

Hvilke char værdier har fil og DB ?

Er filen i UTF-8 ?

Er feltet en VARCHAR eller en NVARCHAR ?
Avatar billede Syska Mester
14. januar 2009 - 03:35 #5
http://syska.dk/upload/capper-47.png - Der char værdierne

Filen er UTF-8 ... har jeg set via headers hvor den bliver hentet fra.
http://s4.travian.dk/map.sql

DB er et varchar(50) felt.
Avatar billede Syska Mester
14. januar 2009 - 04:02 #6
hehe, den postede selvom exp.dk er nede til vedligeholdelse ... utroligt de ikke kan få lavet sitet så det ikke skal være nede :-s tsk tsk.

Men jeg vil daffe i seng og håber på godt svar i morgen når jeg står op :-p

Sådan et lille problem kan give sådan et besvær :-(
Avatar billede arne_v Ekspert
14. januar 2009 - 04:18 #7
Hvis du skal have tegn >255 så er NVARCHAR nok en god ide !!
Avatar billede Syska Mester
14. januar 2009 - 04:19 #8
dvs ... du siger at mit problem bliver løst hvis jeg bruger nvarchar ?

men når det blvier printet ud ser det jo rigtigt ud ... nu er er der nogen der ikke hænger sammen mere i min hjerne.
Avatar billede arne_v Ekspert
14. januar 2009 - 14:52 #9
Jeg vil ikke garantere det.

Men VARCHAR er beregnet til tegn 0-255 og NVARCHAR er beregnet til tegn 0-65535.

Du har tegn over 255.

Det er ihvertfald vaerd at forsoege med NVARCHAR.
Avatar billede Syska Mester
14. januar 2009 - 21:09 #10
No ... still the same ... men så må jeg jo bare convertere mit UTF8 til Unicode, men der er noget som går galt.

Taget fra MSDN ... men der går noget galt ... Unicode er 4 byte, UTF8 er 2 ....

private string ConvertUTF8ToUnicode(string input)
        {
            // Convert the string into a byte[].
            byte[] utf8Bytes = unicode.GetBytes(input);

            // Perform the conversion from one encoding to the other.
            byte[] unicodeBytes = Encoding.Convert(utf8, unicode, utf8Bytes);

            // Convert the new byte[] into a char[] and then into a string.
            // This is a slightly different approach to converting to illustrate
            // the use of GetCharCount/GetChars.
            char[] unicodeChars = new char[unicode.GetCharCount(unicodeBytes)];
            unicode.GetChars(unicodeBytes, 0, unicodeBytes.Length, unicodeChars, 0);
            string unicodeString = new string(unicodeChars);

            return unicodeString;
        }
Avatar billede arne_v Ekspert
14. januar 2009 - 21:13 #11
Unicode er strenget taget ikke bytes, men naar man siger unicode mener man normalt 2 bytes
per tegn. UTF-8 er et variabel antal bytes per tegn - typisk 1 eller 2 paa vores laengdegrader.

Og jeg tror slet ikke paa konverterings loesningen. Du skal finde ud af hvor det gaar
galt i data.
Avatar billede Syska Mester
14. januar 2009 - 21:16 #12
Ups ... i toppen der var en fejl ...

unicode.GetBytes skal selvf være utf8.GetBytes

http://syska.dk/upload/capper-49.png

Men nu går det da helt galt ... nu bliver mit navn ... i dette tilfælde gemt som et "a" og ikke dette mærkelige "a" som du kan se på billedet.

// ouT
Avatar billede Syska Mester
14. januar 2009 - 21:22 #13
Hvad er det Collation præcis ?

http://help.travian.dk/index.php?type=faq&mod=230 under "Hvilket format har dataene?"

UTF8 ... og der er faktisk ingen spille verdener 2 tilbage ...

Så jeg går ud fra at jeg skal lede efter fejler på min SQL 2008 server.
Avatar billede arne_v Ekspert
14. januar 2009 - 21:29 #14
collation er "sorterings raekkefoelge"
Avatar billede Syska Mester
14. januar 2009 - 21:49 #15
En simple test inde fra SSMS:
table med ID og Name:
CREATE TABLE [dbo].[TestLort](
    [TestID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL
) ON [PRIMARY]

INSERT INTO TestLort ([Name]) VALUES ('.α');
SELECT * FROM TestLort WHERE Name = '.a'

Min select kan finde overstående ... WHY :-s
Avatar billede Syska Mester
14. januar 2009 - 21:49 #16
Ja okay ... Eksperten gør så et eller andet ved mit bogstav, men det er "alpha" mener jeg.

// ouT
Avatar billede Syska Mester
14. januar 2009 - 22:03 #17
Okay ... måske fundet en lille fejl i mit system som skal rettes først.

Hvordan kan jeg dynamisk lave min stringe om så min server forstår det ?

Før brugte jeg LINQ til at lave det ... og der kom det rigtigt ind. Så imens har de 2 ting her næsten kommet frem, da jeg havde undersøgt andre måder at lave mine updates af de navne på ...

ved at bruge MERGE INTO ... hviket gav en speed up på en factor 12. great, men intet er så godt at det ikke kommer uden problemer ... så hvordan skal jeg få de special tegn med over ?

Det skal ligesom løses først kan jeg regne ud :-p.

Min query kommer til at se ca. sådan her ud:
MERGE INTO Villages AS V
USING (Values
(7,34,34,'.α',1)
) I([SID], [VID], [UID], [Name], [Active])
ON (V.[SID] = I.[SID] AND V.[VID] = I.[VID])
WHEN MATCHED THEN
UPDATE SET [UID] = I.[UID], [Name] = I.[Name], [Active] = I.[Active];

Men ... undervejs bliver mit alpla tegn åbenbart lavet om :-s

I'm lost here.
Avatar billede Syska Mester
14. januar 2009 - 22:10 #18
Nu er det et import som jeg kun selv har adgang til ... men normalt sørger sqlparameters jo for at beskytte mod sql injection ...  men hvad gør man sådan manuelt selv ?

Der er jo ligepludselig en del problemer man skal tage stilling til ... :-(

btw, der skal nok komme lidt ekstra point på :-)
Avatar billede arne_v Ekspert
14. januar 2009 - 22:12 #19
Jeg kan proeve at teste lidt selv naar jeg kommer hjem fra arbejde.
Avatar billede Syska Mester
14. januar 2009 - 22:53 #20
ahhh

UPDATE t1 SET Name = N'test' WHERE ID = 10 :-)

og ja, det var vist det nvarchar og en utrolig omgang ... stirre sig blind på det.

smid svar.
Avatar billede websmith Nybegynder
14. januar 2009 - 22:59 #21
Har du konverteret din kolonne til nvarchar? Hvis ikke så vil dine "special" tegn ihvertfald ikke fungere.

Din "TestLort" tabel bruger jo nvarchar, som er unicode og varchar bruger så vidt jeg husker ANSI codepagen fra windows, hvilket måske ikke har de karakterer du ønsker.

Den fil du importerer fra, har du prøvet som test at skrive den til en anden fil igen, og sammenligne om du får skrevet det korrekt der - hvis det ikke er tilfældet, så kan det skyldes encoding af filen som er forkert.

En alternativ grund til at den ikke finder noget er at SQL serveren fortolker dit alpha tegn som et a. Dette kan skylde Collation kombineret med en varchar.

Jeg ville teste med en NVARCHAR kolonne i en tabel som bruger SQL_Latin1_General_CP1_CI_AS som collation. Det er ihvertfald altid den collation jeg bruger og jeg har ikke haft de problemer du angiver.

Hvad er det egenligt du prøver at løse med den MERGE syntax i din SQL?
Avatar billede websmith Nybegynder
14. januar 2009 - 22:59 #22
hmm, godt du selv fandt ud af det imens jeg skrev :)
Avatar billede arne_v Ekspert
15. januar 2009 - 01:09 #23
svar
Avatar billede Syska Mester
15. januar 2009 - 14:23 #24
Hej,

Websmith:
Jeg løset et UPDATE af mange rows på en smart måde ... jeg har i hvert fald ikke kunne finde andre og bedre alternativer end det. Så hvis du har en hvorpå jeg kan update 30.000 rows hurtigt, så kører jeg gerne.

Arne:
Men ... jeg må sige at de guide lines som travian selv har skrevet, nok fra start har kastet mig i den forkerte retning ... DAMM them to hell. Men så længe det er løst er jeg glad ... det får vi i hvert fald at se senere i dag :-). Der skulle helst ikke være så mange navne på de russiske og kinesiske server som har ændret navn.

Insert bliver lavet med SqlBulkCopy ... super hurtig.

But still ... det hele hænger lidt i svinget med Update ... :-(

// ouT
Avatar billede arne_v Ekspert
16. januar 2009 - 00:30 #25
UPDATE bør ikke være så slem med relevante index på tabellen. Og hvis du vil give en hjælpende
hånd til SQLServer, så bundter du en masse updates i en enkelt transaktion.
Avatar billede Syska Mester
16. januar 2009 - 03:37 #26
Ikke destro mindre skal jeg have lavet omkring 6 mill af dem ... når jeg får tilføjet de sidste servere hvor der også skal hentes data fra ...

Pt tager det ca. 48 mins at sammenligne mine data hvoraf netop det meste af tiden bliver brugt på UPDATE af forskellige ting. Tror jeg sparede 40% af tiden ved at lave updates på den her måde, så det synes jeg var et godt hop i den rigtige retning.

Men du siger det kan gøres hurtigere ... så tester jeg gerne. Men for ikke at jeg laver nogen mærkelige ting, så vil det være fedt med et eksempel på hvordan det kan gøres. Start til slut, da jeg synes jeg har været alt igennem. Det sidste jeg har lavet har klart gjort den største forskel.

Jeg har nogen klasser hvor mine data bliver indlæst og sammenlignet ... og det registere jeg så ... og kan deraf oprette mine quries som det skal være ... for at optimere det hele. Sådan det kun er ændret data jeg updatere, og nyt data bliver så indsat.

Der er rigtige indexes på ... slår op via 2 columns som sammen er PK. Første er ServerID og andet er det unikke id på netop den server.

Går ud fra at det er fint nok at bruge ens PK, den er jo også et index.

Det må vist være alt for denne gang.
Avatar billede Syska Mester
17. januar 2009 - 20:23 #27
mere input til sidste post ?
Avatar billede arne_v Ekspert
17. januar 2009 - 20:31 #28
Det er ikke særligt klart for mig hvad du præcist gør.

Har du prøvet at bundte mange updates i en enkelt transaktion ?
Avatar billede Syska Mester
17. januar 2009 - 20:56 #29
Som sagt ... har prøvet det meste af hvad jeg kender til ... og eneste som giver et bedre resultat er MERGE.

Men ... da jeg intet præcist eksemple har for hvad du præcist vil have mig til at prøve, går jeg ud fra det skulle være noget ala ( taget fra MSDN ):
public void RunSqlTransaction(string myConnString)
{
    SqlConnection myConnection = new SqlConnection(myConnString);
    myConnection.Open();

    SqlCommand myCommand = myConnection.CreateCommand();
    SqlTransaction myTrans;

    // Start a local transaction
    myTrans = myConnection.BeginTransaction();
    // Must assign both transaction object and connection
    // to Command object for a pending local transaction
    myCommand.Connection = myConnection;
    myCommand.Transaction = myTrans;

    try
    {
      myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
      myCommand.ExecuteNonQuery();
      myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
      myCommand.ExecuteNonQuery();
      myTrans.Commit();
      Console.WriteLine("Both records are written to database.");
    }
    catch(Exception e)
    {
      try
      {
        myTrans.Rollback();
      }
      catch (SqlException ex)
      {
        if (myTrans.Connection != null)
        {
          Console.WriteLine("An exception of type " + ex.GetType() +
                            " was encountered while attempting to roll back the transaction.");
        }
      }
   
      Console.WriteLine("An exception of type " + e.GetType() +
                        " was encountered while inserting the data.");
      Console.WriteLine("Neither record was written to database.");
    }
    finally
    {
      myConnection.Close();
    }
}

og så sætte min CommandText til:
UPDATE t1 SET Pop = 10 WHERE SID = 7 AND VID = 20;
UPDATE t1 SET Pop = 10 WHERE SID = 7 AND VID = 20;
UPDATE t1 SET Pop = 10 WHERE SID = 7 AND VID = 20;

Og så bundle dem 1000 stk sammen ... er dette korrekt forstået ?
Avatar billede arne_v Ekspert
17. januar 2009 - 21:46 #30
BeginTransaction
1000 x ExecuteNonQuery
Commit

nogle gange giver det en ganske pæn performance forbedring
Avatar billede Syska Mester
17. januar 2009 - 21:54 #31
Jamen ... så vil jeg da prøve overstående, dog med den lille rettelse bare at sende en command af gangen ... vender tilbage, men er ret sikker på at jeg har prøvet det og at det var dårlige performance. Derfor var jeg glad da jeg kunne se dette virkede godt :-)

Men ved at være så lang tid siden at det måske har været på en lidt anden måde ... can't remember ...

Vender tilbage når det er testet.
Avatar billede Syska Mester
17. januar 2009 - 22:58 #32
http://syska.dk/upload/capper-52.png

Koden som er brugt er følgende ... gør jeg noget forkert her? Følgende update som du kan se i overstående ss, tager 1 min 35 sek, hvilket er ca. 19 gange langere end når jeg gør det med min MERGE ... :-s ...

Hvordan skal det lige laves for at kunne være så hurtig ? Jeg er helt tabt ...

StopWatch sw = new StopWatch();

            Regex reg = new Regex("(\\d+),(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+),'(.*)',(\\d+),'(.+)',(\\d+),'(.*)',(\\d+)", RegexOptions.Compiled);
            Match m;
            string s;
            List<WorldRow> list = new List<WorldRow>();
            WorldRow wr;
            int lines = 0;


            sw.Start();
            using (StreamReader sr = new StreamReader(@"C:\temp\DK\s4.travian.dk\2009-01-17.sql", Encoding.UTF8))
            {
                while (!sr.EndOfStream)
                {
                    lines++;

                    s = sr.ReadLine();
                    m = reg.Match(s);

                    if (reg.IsMatch(s))
                    {
                        wr = new WorldRow();

                        wr.CoorID = int.Parse(m.Groups[1].Value);
                        wr.X = short.Parse(m.Groups[2].Value);
                        wr.Y = short.Parse(m.Groups[3].Value);
                        wr.TID = byte.Parse(m.Groups[4].Value);
                        wr.VID = int.Parse(m.Groups[5].Value);
                        wr.VillageName = m.Groups[6].Value;
                        wr.UID = int.Parse(m.Groups[7].Value);
                        wr.PlayerName = m.Groups[8].Value;
                        wr.AID = (int.Parse(m.Groups[9].Value) == 0) ? null : (int?)int.Parse(m.Groups[9].Value);
                        wr.AllianceName = m.Groups[10].Value;
                        wr.Population = short.Parse(m.Groups[11].Value);

                        list.Add(wr);
                    }
                }
            }



            Console.WriteLine("Lines: {0}, Reg Hits: {1}", lines, list.Count);
            sw.Stop("Parse input");

            sw.Start();
            SqlConnection myConnection = new SqlConnection(connString);
            myConnection.Open();

            SqlCommand myCommand = myConnection.CreateCommand();
            SqlTransaction myTrans = null;

            myCommand.CommandText = "UPDATE Villages SET Population = @Population WHERE SID = @SID AND VID = @VID";
            SqlParameter pop = myCommand.Parameters.Add("@Population", System.Data.SqlDbType.SmallInt);
            SqlParameter sid = myCommand.Parameters.Add("@SID", System.Data.SqlDbType.SmallInt);
            SqlParameter vid = myCommand.Parameters.Add("@VID", System.Data.SqlDbType.Int);
            sid.Value = 7;
            try
            {
                for (int i = 0; i <= (list.Count / 1000); i++)
                {
                    // Start a local transaction
                    myTrans = myConnection.BeginTransaction();
                    // Must assign both transaction object and connection
                    // to Command object for a pending local transaction
                    // myCommand.Connection = myConnection;
                    myCommand.Transaction = myTrans;
                    foreach (WorldRow row in list.Skip(1000 * i).Take(1000))
                    {
                        pop.Value = row.Population;
                        vid.Value = row.VID;
                        myCommand.ExecuteNonQuery();
                    }
                    Console.WriteLine("First commit");
                    myTrans.Commit();   
                }
               
            }
            catch (Exception e)
            {
                try
                {
                    myTrans.Rollback();
                }
                catch (SqlException ex)
                {
                    if (myTrans.Connection != null)
                    {
                        Console.WriteLine("An exception of type " + ex.GetType() +
                                          " was encountered while attempting to roll back the transaction.");
                    }
                }

                Console.WriteLine("An exception of type " + e.GetType() +
                                  " was encountered while inserting the data.");
                Console.WriteLine("Neither record was written to database.");
            }
            finally
            {
                myConnection.Close();
            }
            sw.Stop("UPDATE");
Avatar billede arne_v Ekspert
18. januar 2009 - 01:12 #33
Hvis MEREGE er det hurtigste, så er det jo det.
Avatar billede Syska Mester
18. januar 2009 - 02:26 #34
ja ... men som sagt ... jeg kunne nemt have lavet noget galt ... også i overstående kode ... Databaser i den her størrelse er meget ny for mig ... så at noget kan gøres hurtigere på andre måder er sikkert ...

Normalt har jeg kun nogen få rows at updatere, men dette er jo i en helt anden størrelse.

// ouT
Avatar billede arne_v Ekspert
18. januar 2009 - 04:29 #35
Din TX kode er OK (jeg ville have nøjes med en neklet for løkke og commit når modulus 1000 var 0), men
det betyder næppe noget for total performance.

Jeg tror at konklusionen er at din merge er det bedste.

Jeg har ihvertfald ikke flere ideer.
Avatar billede Syska Mester
11. marts 2010 - 09:18 #36
smid et svar
Avatar billede arne_v Ekspert
11. marts 2010 - 15:57 #37
moi ?
Avatar billede Syska Mester
11. marts 2010 - 17:03 #38
og ham den anden ... web
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
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

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