Вычесть число из таблицы в SQL

В общем у меня есть таблица с ресурсами золото, дерево, камень, руда и женщины, предположим. Когда я строю мельницу, ну выполняю метод build мельница я хочу чтобы ресурсы из базы данных вычитались, ну минус 10 золота и минус 20 дерева. Ну или нужно больше золота, как сделать так чтобы сколько бы там ни было ресурса, например золота, то вычиталось бы из этой суммы 10, а то у меня получается, что я просто перезаписываю 100 на 10. А чтобы узнать сколько у меня сейчас в таблице золота я должен выполнить ешё один запрос и потом уже получив 100 я вычту 10, это любой дурак сможет, а вот как сделать, чтобы в базу данных уходил statment что мол вычти ка 10 из таблицы, а если что пойдёт не так, ты мол просигналь, что нужно золота больше, можно ли такое диво сотворить?

connectionString = ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString;
SqlConnection con = new SqlConnection(connectionString.ToString());
con.Open();
string sql = $@"SELECT [Wood]
            FROM [GG].[dbo].[Villages]
            where OwnerUser = '{user.Name}' and Name = '{user.Village}'";
SqlCommand cmd = new SqlCommand(sql, con);
int amount = (int)cmd.ExecuteScalar();

string sql1 = $@"Update [GG].[dbo].[Villages]
            SET Wood = '{amount - wood}'
            WHERE OwnerUser = '{user.Name}' and Name = '{user.Village}'; ";
SqlCommand cmd1 = new SqlCommand(sql1, con);

cmd1.ExecuteNonQuery();
con.Close();

Ответы (1 шт):

Автор решения: Alexander Petrov

Не нужно предварительно считывать значение из БД, если нужно просто изменить его.

В запросе UPDATE можно указать формулу изменения: SET Wood = Wood - @amount.

using (SqlConnection con = new SqlConnection(_connectionString))
{
    con.Open();

    string updateSql = @"
        UPDATE [GG].[dbo].[Villages]
        SET [Wood] = [Wood] - @amount
        WHERE [OwnerUser] = @name and [Name] = @village";

    using (SqlCommand cmd = new SqlCommand(updateSql, con))
    {
        cmd.Parameters.Add("amount", SqlDbType.Int).Value = amount;
        cmd.Parameters.Add("name", SqlDbType.NVarChar).Value = user.Name;
        cmd.Parameters.Add("village", SqlDbType.NVarChar).Value = user.Village;

        int rowsAffected = cmd.ExecuteNonQuery();
    }
}

Не следует строку соединения получать каждый раз при вызове метода. Задайте её один раз в конструкторе класса полю и далее используйте его.

Обязательно освобождайте ресурсы, оборачивая их в using.

Используйте параметризованные запросы. Это защитит от sql-инъекций и увеличит производительность.
Я не знаю, какие именно типы в таблице, поэтому замените SqlDbType.Int, SqlDbType.NVarChar на правильные.


Если при этом необходимо обеспечить, чтобы количество ресурса не ушло в минус, можно наложить ограничение (constraint) на колонку таблицы.

create table Villages (
    -- другие колонки

    Wood int check (Wood > 0)
)

CHECK Constraints
Create Check Constraints

Во время выполнения запрос СУБД выбросит сообщение об ошибке. В коде C# нужно обработать исключение:

try
{
    int rowsAffected = cmd.ExecuteNonQuery();                        
}
catch (System.Data.SqlClient.SqlException e)
{
    // Мало дерева!
}
→ Ссылка