0

I have a big SQL text file where I have a lot of SQL commands to create tables, columns, etc.

Example row(s):

IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'xcal_views') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) 
   CREATE TABLE xcal_views (lid INT NOT NULL);
GO

IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'xcal_views_actors') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) 
   CREATE TABLE xcal_views_actors (lid INT NOT NULL);
GO

IF NOT EXISTS ( SELECT * FROM dbo.syscolumns, dbo.sysobjects WHERE [dbo].[syscolumns].[name] = 'xlactor' AND [dbo].[sysobjects].[id] = [dbo].[syscolumns].[id] AND [dbo].[sysobjects].[id] = object_id(N'xcal_views_actors') AND OBJECTPROPERTY([dbo].[sysobjects].[id], N'IsUserTable') = 1 ) 
   ALTER TABLE [dbo].[xcal_views_actors] ADD xlactor INT NULL;
GO

IF NOT EXISTS ( SELECT * FROM dbo.syscolumns, dbo.sysobjects WHERE [dbo].[syscolumns].[name] = 'lparentid' AND [dbo].[sysobjects].[id] = [dbo].[syscolumns].[id] AND [dbo].[sysobjects].[id] = object_id(N'xcal_views_actors') AND OBJECTPROPERTY([dbo].[sysobjects].[id], N'IsUserTable') = 1 ) 
   ALTER TABLE [dbo].[xcal_views_actors] ADD lparentid INT NULL;
GO

IF NOT EXISTS ( SELECT * FROM dbo.sysobjects WHERE parent_obj = (SELECT id FROM dbo.sysobjects WHERE id = object_id(N'xcal_views_actors') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) AND OBJECTPROPERTY(id, N'IsPrimaryKey') = 1) 
   ALTER TABLE [dbo].[xcal_views_actors] 
   ADD CONSTRAINT [CT_00000501] PRIMARY KEY CLUSTERED (lid ASC);
GO

IF NOT EXISTS ( SELECT * FROM [sys].[indexes] i INNER JOIN [sys].[objects] o ON o.object_id = i.object_id AND o.name = 'xcal_views_actors' WHERE i.name = 'parent_id' ) 
   CREATE INDEX parent_id ON xcal_views_actors (lparentid ASC)
GO

Between each command I have a GO in extra line to separate the commands.

If I run the whole patch.sql file from SQL Server Management Studio all commands are executed and works fine.

In .NET I read the whole text file, then split them with 'GO' and execute each SQL command against the database.

Now the strange thing: some of the commands don't get executed. And I can't find out why.

This is the method that does the job:

private static void patchDatabase(string connection, string sqlfile)
{
  var defaultEncoding = Encoding.Default;
  using (FileStream fs = File.OpenRead(sqlfile))
  {
    defaultEncoding = TextFileEncodingDetector.DetectTextFileEncoding(fs, defaultEncoding, 1024);
  }
  //Console.WriteLine(string.Format("File {0} using encoding: {1}",sqlfile, defaultEncoding));

  var dbPatch = new StreamReader(sqlfile, defaultEncoding);
  string sqlPatch = dbPatch.ReadToEnd();
  dbPatch.Close();
  string[] stringSeparators = new[] {"GO"};
  string[] sqlPatches = sqlPatch.Split(stringSeparators, StringSplitOptions.None);

  if (connection != null && sqlPatch.Length > 0)
  {
    Console.WriteLine(string.Format("Executing {0} statements from {1}", sqlPatches.Length, sqlfile));

    using (var cnn = new SqlConnection(connection))
    {
      cnn.Open();
      foreach (var sql in sqlPatches)
      {
        if (String.IsNullOrEmpty(sql))
          continue; // Not a real sql statement, use next

        using (var cmd = new SqlCommand(sql, cnn))
        {
          try
          {
            cmd.CommandTimeout = 120;
            cmd.ExecuteNonQuery();
            //int results = cmd.ExecuteNonQuery();
            //if (results < 1)
            //  Console.WriteLine(String.Format("Failed:\nResult: {0}\n{1}",results, sql));
          }
          catch (Exception ex)
          {
            Console.WriteLine("Execution error!\n\n" + sql + "\n\n\n" + ex);
          }
        }
      }
      cnn.Close();
    }

  }
}

It looks like my function splutters...

My current textfile has around 6.000+ lines.

Any idea what I do wrong?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
YvesR
  • 5,922
  • 6
  • 43
  • 70
  • Curious why you have this big script file, that could be run as a single script, and feel the need to split it up by GO and call each batch independently? Why not just run the whole script in one shot? Also I'm not sure I understand why you can't figure out what happened... where is your Control.WriteLine output going? How do you know some commands didn't run? – Aaron Bertrand May 25 '12 at 16:23
  • If there is not a single statement that requires to be the first in batch you can replace NewLine+GO+NewLine with newline and run the script; else you better look at [this SO answer](http://stackoverflow.com/questions/3102768/ado-net-and-executenonquery-how-to-use-ddl). – Nikola Markovinović May 25 '12 at 17:37
  • @AaronBertrand I split it because I want to execute each statement and get a result if it fails. Also I want to make sure all other statements continues if one statement fail. The method is called from a cmd.exe line tool, so I get the results or redirect it to a text file. Another reason is the timeout. Some of my statements are also higher complex stuff (like procedures that need more time). Calling it from management studio is just for testing and normally all statements are called from my command tool. – YvesR May 25 '12 at 17:43
  • If you have stored procedures that take more than 2 minutes to compile, I'd say that's a completely different problem. Anyway as I suggested your script currently seems to output the results of exceptions to Console.WriteLine. Have you reviewed this output to see where the problems are? – Aaron Bertrand May 25 '12 at 17:45
  • @NikolaMarkovinović Your link answer has the same approach I do as far I can see. – YvesR May 25 '12 at 17:45
  • @AaronBertrand Yes I reviewed the output. Thats why it is so strange, because e.g. one ADD COLUMN reports an exception that xcal_views is not there so I can't add this column. But the create table statement was called before without error. Addition: I run the script again and it created the tables. The only thing I changed was that I saw that I missed the database user because I restored my database. In the main security settings the user exists and has rights to the database, but inside the db the user was missing. – YvesR May 25 '12 at 17:48
  • @AaronBertrand ... This is strange. I will remove now the user again and test if this can be reproduced. Then it seems to be a security issue that "works half"?! – YvesR May 25 '12 at 17:48

1 Answers1

0

It looks like it wasn't a coding problem at all.

In fact I produced the following situation:

  • I have a database "test_db"

  • I create a user "test_db_user" and make him db_owner He is added in sql server in general "security - roles" with objekt "test_db" as role db_owner He also gets added as user in the database "test_db" - security - user.

  • Now it comes: I restored the database again after some tests. The user is not anymore listed in "test_db" - security - user but still configured as db_owner in general.

Somehow the connection works then but it don't has not full access to the db all the time. Can't really know what is going wrong. From the management studio I ever used admin account for starting the sql batches, that is the reason it worked all the time there.

So solution: Make sure the security settings are 100% correct and then it works :S

Another problem was my stupidity :S!!! I had some create table statements without dbo. schema before the table so the different user created a different schema name for the table.

Thanks to all for the feedback.

YvesR
  • 5,922
  • 6
  • 43
  • 70