-1

I have the following code to check if a SQL table exists:

using (var conn = new SqlConnection(SqlServerConnectionString))
{
    conn.Open();
    var selectQuery = $"SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{tableName}'";
    var cmd = new SqlCommand(selectQuery, conn);
    var reader = cmd.ExecuteReader();

    while (reader.Read())
    {
        var count = reader.GetInt32(0);
        return count > 0;
    }
    reader.Close();
    conn.Close();
}

And it works fine. Is there a way to update this line to something more easily readable or easy to understand?

var count = reader.GetInt32(0);

Dale K
  • 25,246
  • 15
  • 42
  • 71
user989988
  • 3,006
  • 7
  • 44
  • 91

2 Answers2

2

This should work, and also fixes the nasty sql injection issue:

public bool TableExists(string tableName)
{
    var selectQuery = "SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName";
    using (var conn = new SqlConnection(SqlServerConnectionString))
    using (var cmd = new SqlCommand(selectQuery, conn))
    {
        cmd.Parameters.Add("@TableName", SqlDbType.NVarChar, 128).Value = tableName;
        conn.Open();

        int result = (int?)cmd.ExecuteScalar() ?? 0;
        return result > 0;
    }
}

But you still have this line that is somewhat cryptic:

int result = (int?)cmd.ExecuteScalar() ?? 0;

You could expand it into easier code like this:

public bool TableExists(string tableName)
{
    var selectQuery = "SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName";
    using (var conn = new SqlConnection(SqlServerConnectionString))
    using (var cmd = new SqlCommand(selectQuery, conn))
    {
        cmd.Parameters.Add("@TableName", SqlDbType.NVarChar, 128).Value = tableName;
        conn.Open();

        object result = cmd.ExecuteScalar();
        if (result == null) return false;

        return ((int)result) > 0;
    }
}

Newer versions of C# can shorten this again with Pattern Matching:

public bool TableExists(string tableName)
{
    var selectQuery = "SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName";
    using (var conn = new SqlConnection(SqlServerConnectionString))
    using (var cmd = new SqlCommand(selectQuery, conn))
    {
        cmd.Parameters.Add("@TableName", SqlDbType.NVarChar, 128).Value = tableName;
        conn.Open();

        return (cmd.ExecuteScalar() is int result && result > 0);
    }
}

Or more code, but maybe a little simpler to understand:

public bool TableExists(string tableName)
{
    var selectQuery = "SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName";
    using (var conn = new SqlConnection(SqlServerConnectionString))
    using (var cmd = new SqlCommand(selectQuery, conn))
    {
        cmd.Parameters.Add("@TableName", SqlDbType.NVarChar, 128).Value = tableName;
        conn.Open();

        if (cmd.ExecuteScalar() is int result)
        {
           return result > 0;
        }
    }
    return false;
}

But I'm guessing if you don't like the other code you won't like pattern matching, either.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 1
    `COUNT(*)` without a `GROUP BY` is guaranteed to always return one row which cannot be null (it returns `0`) so in this particular instance you don't need to do any of the null-checking – Charlieface Apr 13 '22 at 09:43
1

There are several different ways to check if an table (or any other object) exists in the database.

This are very similar to search for every kind of object or just for tables:

SELECT COUNT(*) FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test]', N'U')

SELECT COUNT(*) FROM sys.tables WHERE object_id = OBJECT_ID(N'[dbo].[test]', N'U')

SELECT COUNT(*) FROM sys.objects WHERE name = N'test' AND schema_id = SCHEMA_ID(N'dbo') AND type = N'U'

or very short form this will return null if it does not exists or the object_id if it exists

SELECT OBJECT_ID(N'[dbo].[test]', N'U')

If you just want your c# code shorter/cleaner than you could do this:

using (var conn = new SqlConnection (SqlServerConnectionString))
{
  conn.Open ();
  var selectQuery = $"SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName";
  var cmd = new SqlCommand (selectQuery, conn);
  cmd.Parameters.AddWithValue ("@tablename", tableName);
  var result = (int)cmd.ExecuteScalar ();

  return result > 0;
}

EDIT: as discussed in the comments I removed conn.Close()

using is just a short form for this to make sure that conn is disposed even if an exception occurs half way through the code

SqlConnection conn = null;
try
{
  conn = new SqlConnection (SqlServerConnectionString))

  conn.Open ();
  var selectQuery = $"SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName";
  var cmd = new SqlCommand (selectQuery, conn);
  cmd.Parameters.AddWithValue ("@tablename", tableName);
  var result = (int)cmd.ExecuteScalar ();

  return result > 0;
}
finally
{
  conn?.Dispose ();
}
  • Thank you! So if I have a sql query: SELECT PersonnelNumber, AzureObjectId FROM {tableName} WHERE {query} can I parameterize tableName as well as query? – user989988 Apr 13 '22 at 16:45
  • @user989988 no you can only put values in variables not names – Johannes Krackowizer Apr 13 '22 at 17:20
  • I'm sorry I didn't understand - you mean we can only parameterize whatever comes after = {tableName}? In the above case, I can't parameterize tableName & query in SELECT PersonnelNumber, AzureObjectId FROM {tableName} WHERE {query} – user989988 Apr 13 '22 at 17:25
  • 1
    you can only parameterize values, not names of columns, tables etc. so you need to write SELECT PersonnelNumber, AzureObjectID, FROM [dbo].[tableXY] WHERE column123 = @@var1 AND colum456 = @@var2 There is a concept called "Dynamic SQL" but thats a little to complicated to describe it in a comment and you have the same security implications in Dynamic SQL like if you use a string where you build your SQL statement with user input – Johannes Krackowizer Apr 13 '22 at 17:35
  • Great, thank you! Can you send any links regarding parameterizing Dynamic SQL queries? – user989988 Apr 13 '22 at 18:00
  • 1
    you could start at the official [learn.microsoft.com](https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver15) you will find ther description and examples. if you have a specific question you could open your own question – Johannes Krackowizer Apr 13 '22 at 18:05
  • Hi I tried using your code. However I see a connection timeout exception while running. What could be the issue? – user989988 Apr 14 '22 at 06:08
  • that probably means that someting with your connection string is wrong but without detailed description of the error it*s impossible to tell whats wrong. – Johannes Krackowizer Apr 14 '22 at 07:34
  • Please see this - https://stackoverflow.com/questions/71867262/connection-timeout-expired-on-using-sql-connection – user989988 Apr 14 '22 at 07:38
  • One more question - if we use using (var conn), connection gets automatically closed, correct? is conn.close() required? – user989988 Apr 14 '22 at 07:40
  • 1
    as you can read [here](https://stackoverflow.com/questions/1195829/do-i-have-to-close-a-sqlconnection-before-it-gets-disposed#:~:text=No%2C%20it%20is%20not%20necessary,a%20connection%20before%20calling%20Dispose.&text=is%20not%20useful-,Show%20activity%20on%20this%20post.,Dispose%20on%20the%20SqlConnection%20class.) you dont nede explicit close a connection if you dispose the connection – Johannes Krackowizer Apr 14 '22 at 08:02
  • Ok, in the example code you mentioned in the solution above - I can add call.Dispose() instead of call.Close() correct? – user989988 Apr 14 '22 at 08:11
  • 1
    the closing } of using is calling dispose so you do not need to call it. For more information see [learn.microsoft.com](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally) – Johannes Krackowizer Apr 14 '22 at 08:14
  • I see that I get a connection timeout error sometimes while using your code. Any idea what I am missing? Connection Timeout Expired. The timeout period elapsed during the post-login phase. The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=17; handshake=134; [Login] initialization=0; authentication=0; [Post-Login] complete=29204; The wait operation timed out. – user989988 Apr 25 '22 at 20:23
  • I assume you get that error while `con.Open ()` that would indicate that you have some issues with your sql server, connection string, network, firewall etc. – Johannes Krackowizer Apr 26 '22 at 08:00
  • Could you please let me know how to check that? Here is my connection string: Server=tcp:severname,1433;Initial Catalog=catalog;Persist Security Info=False;User ID=user id;Password=password;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; and I see that this is only happening sometimes. – user989988 Apr 26 '22 at 17:51
  • On first glance there is nothing wrong with your connection string. Perhaps this [post](https://stackoverflow.com/questions/1421978/help-troubleshooting-sqlexception-timeout-expired-on-connection-in-a-non-load) can help you – Johannes Krackowizer Apr 26 '22 at 20:11