2

I have working code which I have listed below here it does exactly what I want. However, I would like to minimize it down so I only call the DB once instead of twice. I currently have the query written to only bring back 1 record that has 1 column.

This column brings back a date which I use in my code. The issues is I also need to bring back the user as well. Which I made work by basically using the same query but have it bring back the user rather than the date.

THE BIG QUESTION: I have the query to use to bring back both pieces of information, however, I am not sure how to get the information back to use in my code? when there is more that one column being returned from query. side note the new query still only brings back one record but with 2 columns.

THE WORKING CODE: I would add in "UpdatedBy" to last select statement to add the 2nd column.

SqlConnection conn = new SqlConnection(@"Data Source=(localdb)\......");
                SqlCommand cmd = conn.CreateCommand();

                string updatedOn;
                conn.Open();
                cmd.CommandText = ("USE CORNERSTONE DECLARE @SID int SET @SID = " + SupplierId + "; "  +
                "with MaxDate as " +
                "(select UpdatedOn, UpdatedBy, SupplierId from CORNERSTONE.Suppliers.Supplier where SupplierId = @SID " +
                "UNION " +
                "select UpdatedOn, UpdatedBy, SupplierId from Suppliers.CompanyLocation where SupplierId = @SID " +
                "UNION " +
                "select UpdatedOn, UpdatedBy, SupplierId from Suppliers.AssignedContact where SupplierId = @SID) " +
                "select UpdatedOn " +
                "from MaxDate " +
                "where UpdatedOn = (Select MAX(UpdatedOn) from MaxDate)");

                updatedOn = cmd.ExecuteScalar().ToString();
                DateTime ParsedDateTime = DateTime.Parse(updatedOn);
                AuditUpdatedOn = ParsedDateTime;

                conn.Close();
joakimriedel
  • 1,801
  • 12
  • 27
  • btw: you should pass `SID` in as a *parameter* rather than using concatenation - safer *and* more efficient (query plan reuse, etc) - i.e. `cmd.Parameters.AddWithValue("@SID", SupplierId);` (goes back to trying to understand what the code is doing) – Marc Gravell Jun 03 '20 at 13:02
  • also: pro tip - use a verbatim string literal for SQL, then you don't need to keep doing newlines with quotes and `+` just `@" ... "` with the `...` *including* newlines – Marc Gravell Jun 03 '20 at 13:04
  • NOTED thanks I'll mess around with that. see what I can do to add that in. – RodeoEmpire Jun 03 '20 at 13:04
  • Why are you using ExecuteScalar which only returns one column when you are selecting multiple columns? See : https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.executescalar?view=dotnet-plat-ext-3.1 – jdweng Jun 03 '20 at 13:05
  • well the reasoning for that is i was having a hard time figuring it out how to get info any other way that would actually work I'll check to link to see if I can get more info I failed to find when creaating – RodeoEmpire Jun 03 '20 at 13:07
  • possible duplicate of https://stackoverflow.com/questions/33677191/how-to-get-multiple-rows-from-datareader – joakimriedel Jun 03 '20 at 13:16

1 Answers1

2

I'm assuming that you want the UpdatedBy from the query; if so, this should just be:

cmd.CommandText = @"
with MaxDate as (
    select UpdatedOn, UpdatedBy from CORNERSTONE.Suppliers.Supplier where SupplierId = @SID
    union select UpdatedOn, UpdatedBy from Suppliers.CompanyLocation where SupplierId = @SID
    union select UpdatedOn, UpdatedBy from Suppliers.AssignedContact where SupplierId = @SID
)
select top 1 UpdatedOn, UpdatedBy
from MaxDate 
where UpdatedOn = (Select MAX(UpdatedOn) from MaxDate)
";

However, the problem comes from the fact that you now have two columns to read, so ExecuteScalar isn't useful. There are two options:

  1. use ExecuteReader and use the read API, or
  2. cheat and get a tool to do it all for you

On the second, frankly: this is a perfect scenario for "Dapper":

(var when, var who) = conn.QuerySingle<(DateTime, string)>(@"
with MaxDate as (
    select UpdatedOn, UpdatedBy from CORNERSTONE.Suppliers.Supplier where SupplierId = @SID
    union select UpdatedOn, UpdatedBy from Suppliers.CompanyLocation where SupplierId = @SID
    union select UpdatedOn, UpdatedBy from Suppliers.AssignedContact where SupplierId = @SID
)
select top 1 UpdatedOn, UpdatedBy
from MaxDate 
where UpdatedOn = (Select MAX(UpdatedOn) from MaxDate)
", new { SID = SupplierId });

and then when and who should be populated. You might want to think about whether you need QuerySingleOrDefault in case there are no rows.

Usually, when you use Query[First/Single/etc]<T> with Dapper, the T is mapped using column names (to populate an object model), but it detects value-tuples ((DateTime, string) is a value-tuple) and treats them by position instead.


If you have to use raw ADO.NET - something like the following:

using (var cmd = conn.CreateCommand())
{
    cmd.CommandText = @"
with MaxDate as (
select UpdatedOn, UpdatedBy from CORNERSTONE.Suppliers.Supplier where SupplierId = @SID
union select UpdatedOn, UpdatedBy from Suppliers.CompanyLocation where SupplierId = @SID
union select UpdatedOn, UpdatedBy from Suppliers.AssignedContact where SupplierId = @SID
);
select top 1 UpdatedOn, UpdatedBy
from MaxDate 
where UpdatedOn = (Select MAX(UpdatedOn) from MaxDate)
";
    cmd.CommandType = CommandType.Text;
    var p = cmd.CreateParameter();
    p.ParameterName = "@SID";
    p.Value = SupplierId;
    p.DbType = DbType.Int32;
    cmd.Parameters.Add(p);

    using (var reader = cmd.ExecuteReader())
    {
        if (reader.Read())
        {
            var who = reader.GetDateTime(0);
            var when = reader.GetString(1);
            // *** HERE *** we now know the answers
        }
        else
        {
            // what to do if we don't get anyone?
        }
        do // need to consume entire TDS to
        { // make sure we see errors
            while (reader.Read()) { }
        } while (reader.NextResult());
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks for the help I will try these out along with your other advice and see what happens! – RodeoEmpire Jun 03 '20 at 13:17
  • @RodeoEmpire note: if you use Dapper (the second example), the parameterization is even easier - see example – Marc Gravell Jun 03 '20 at 13:17
  • @RodeoEmpire I also added an example using raw ADO.NET - frankly, my advice here is "just use Dapper" (although I'm perhaps biased, but after 44 million downloads it can't be *entirely* wrong) – Marc Gravell Jun 03 '20 at 13:28