web-dev-qa-db-ja.com

SQL Serverから単一のレコードを正しい方法で取得する

Adoを使用して、IDで単一のレコードを取得しています。観察する:

public async Task<Image> GetImage(int id)
{
    var image = new Image();

    using (SqlConnection conn = new SqlConnection(ConnectionString))
    {
        conn.Open();

        string sql = @" SELECT * FROM Images where id = @id";

        using (SqlCommand comm = new SqlCommand(sql, conn))
        {
            comm.Parameters.AddWithValue("@id", id);

            var reader = await comm.ExecuteReaderAsync();

            int ordId = reader.GetOrdinal("id");
            int ordName = reader.GetOrdinal("name");
            int ordPath = reader.GetOrdinal("path");

            while (reader.Read())
            {
                image.Id = reader.GetInt32(ordId);
                image.Name = reader.GetString(ordName);
                image.Path = reader.GetString(ordPath);
            }

            return image;
        }
    }
}

ご覧のとおり、Whileを使用してレコードを繰り返し処理しています。が意味している間、反復する複数のレコードがあるかもしれないので、これは単一のレコードを取得するための間違った方法かもしれないと思います。 ADOが1行1フィールドのExecuteScalarを持っていることを考慮してください。それらは1行の複数フィールドに対して指定された方法を持っているかもしれません。ADOで単一のレコードを取得するための指定された方法はありますか?

7
Luke101

Whileループを排除することを除いて、私はあなたの現在のアプローチを採用します。確実に1つのレコードのみが返されるようにする場合は、追加のReadを実行して、確実にfalseが返されるようにします。これは、LINQ Single 演算子のセマンティクスに似ています。

_if (!reader.Read())        
    throw new InvalidOperationException("No records were returned.");

image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);

if (reader.Read())
    throw new InvalidOperationException("Multiple records were returned.");
_

データベースのid列が主キー(一意)であると想定すると、SQLクエリでTOP句を指定する必要はありません。 SQL Serverクエリオプティマイザーは、WHERE句が原因で最大で1つのレコードのみが返されると推定します。ただし、id列に主キーまたは一意のインデックス/制約がない場合は、TOP (2)句を発行して、返される行の数を制限する必要があります。余分な一致を検出(およびエラーを発生)できないため、TOP (1)の使用は避けてください。

_string sql = @"SELECT TOP (2) * FROM Images WHERE id = @id"
_
13
Douglas

一度だけ読んだ場合:

using (SqlConnection conn = new SqlConnection(ConnectionString))
{
    conn.Open();

    string sql = @" SELECT id, name, path FROM Images where id = @id";

    using (SqlCommand comm = new SqlCommand(sql, conn))
    {
        comm.Parameters.AddWithValue("@id", id);           

        using (var reader = await comm.ExecuteReaderAsync())
        {
            if (!reader.Read())
                 throw new Exception("Something is very wrong");

            int ordId = reader.GetOrdinal("id");
            int ordName = reader.GetOrdinal("name");
            int ordPath = reader.GetOrdinal("path");

            image.Id = reader.GetInt32(ordId);
            image.Name = reader.GetString(ordName);
            image.Path = reader.GetString(ordPath);

            return image;
        }
    }
}

P.S.: selectステートメントを変更して、必要なフィールドのみを選択し、ステートメントを使用してリーダーをラップしました。

5
Eugene Podskal

この場合、クエリで Top(1) を使用して、データベースから単一のレコードのみを取得できます。

SELECT Top(1) * FROM Images 
where id = @id
order by id desc -- will get the latest record
2
Ehsan Sajjad