Как сделать запрос, используя WHERE и AND без объединения нескольких полей - PullRequest
0 голосов
/ 07 июня 2018

Я работаю над таблицей в Visual Studio, которая запрашивает базу данных SQL.У меня есть параметры, чтобы пользователь мог выбрать дату в пределах начальной и конечной даты или иметь возможность ввести вместо этого имя сервера.

То, что я также хочу, это произойдет, если пользователь хочет выбрать дату Иимя сервера Я хочу, чтобы таблица возвращала только дату с этим конкретным именем сервера, но происходит то, что она возвращает все строки с выбранной датой и все строки с введенным именем сервера.У меня есть 2 строки с одинаковым именем сервера, но у одной есть дата 06/06, а у другой - 06/07.Если я выберу дату 06/07 и имя сервера: SQL1, я хочу, чтобы таблица возвращала только одну строку сервера: SQL1 с датой: 06/07.

SELECT        ServerName, ServiceName, Status, Date
FROM          ServicesStatus
WHERE         (Date = @Date) AND (@Date BETWEEN @StartDate AND @EndDate) OR
              (ServerName = @ServerName) OR
              (Date = @Date) AND (@Date BETWEEN @StartDate AND @EndDate) AND (ServerName = @ServerName)

Вот чтопервый оператор WHERE выглядит, когда я выбираю 06/07/18 в качестве даты:

ServerName:    ServiceName:         Date:
SQL3           Service1             06/07/18
SQL4           Service2             06/07/18
SQL2           Service1             06/07/18
SQL1           Service1             06/07/18

Так выглядит второй оператор WHERE после первого ИЛИ, когда я выбираю SQL1 в качестве имени сервера:

ServerName:    ServiceName:         Date:
SQL1           Service1             06/07/18
SQL1           Service2             06/06/18

И ЗДЕСЬ МОЯ ПРОБЛЕМА! Это выглядит так, когда я пытаюсь выполнить третье утверждение WHERE после второго ИЛИ, потому что оно объединяет дату, которую я выбрал (06/07/18) с именем сервера, которое я ввел, которое является SQL1:

ServerName:    ServiceName:         Date:
SQL3           Service1             06/07/18
SQL4           Service2             06/07/18
SQL2           Service1             06/07/18
SQL1           Service1             06/07/18
SQL1           Service2             06/06/18

Я хочу это так после того, как я выберу вышеуказанные данные (SQL1 и 06/07/18), если они решат отфильтровать их по имени сервера иdate:

ServerName:    ServiceName:         Date:
SQL1           Service1             06/07/18

Я знаю, что это простой запрос, но я просто не могу понять его по некоторым причинам.

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

Если значения @Date и @ServerName равны NULL, если они не предоставлены, то для вас должно работать следующее.Я создал образец таблицы и заполнил ее предоставленными вами данными.Это должно привести к результатам, которые вы ищете.Вы можете настроить DATEDIFF для точности при необходимости.Я также предположил, что начальная дата и конечная дата будут разными интервалами одного и того же дня, поскольку он не был указан.Не стесняйтесь изменять по мере необходимости.

CREATE TABLE ServicesStatus (
id INT,
serverName VARCHAR(255),
ServiceName VARCHAR(255),
Date DATETIME2
);


DECLARE @Date DATETIME2 = '06/07/18'
DECLARE @StartDate DATETIME2 = dateadd(second, 1, dateadd(day, datediff(day, 0, @Date), 0))
    , @EndDate DATETIME2 = dateadd(second, -1, dateadd(day, datediff(day, 0, @Date)+1, 0))
    , @ServerName VARCHAR(255) = NULL

SELECT    ServerName, ServiceName, Date
FROM      ServicesStatus
WHERE     ServerName = COALESCE(@ServerName,ServerName)
AND       (@Date IS NULL OR DATEDIFF(DAY,@StartDate,Date) >= 0)
AND       (@Date IS NULL OR DATEDIFF(DAY,Date,@EndDate) <= 0)
0 голосов
/ 08 июня 2018

Нечто подобное будет работать.Если вы не устанавливаете неизвестные значения в NULL, замените NULL на любое значение, которое вы используете в своих переменных для «пофиг».

Вы не указали значения для @StartDate и @EndDate, поэтомуЯ предполагаю, что они просто действуют как внешние фильтры.

--The Setup
DECLARE @ServicesStatus TABLE ([ServerName] varchar(4), [ServiceName] varchar(8), [Date] datetime)
;

INSERT INTO @ServicesStatus
    ([ServerName], [ServiceName], [Date])
VALUES
    ('SQL3', 'Service1', '2018-06-07 00:00:00'),
    ('SQL4', 'Service2', '2018-06-07 00:00:00'),
    ('SQL2', 'Service1', '2018-06-07 00:00:00'),
    ('SQL1', 'Service1', '2018-06-07 00:00:00'),
    ('SQL1', 'Service2', '2018-06-06 00:00:00')
;

DECLARE @StartDate date = '20180601'
DECLARE @EndDate date = '20180610'
DECLARE @Date date = '20180607'
DECLARE @ServerName nvarchar(4)='SQL1'

--The Code
SELECT        ServerName, ServiceName, Date
FROM          @ServicesStatus
WHERE         (@Date BETWEEN @StartDate AND @EndDate OR @DATE IS NULL) AND
              (
                (ServerName = @ServerName AND @Date IS NULL) OR
                (@ServerName IS NULL AND @Date = date) OR
                (Date= @Date AND ServerName = @ServerName)
               )

С @Servername = NULL и @Date = '20180607' вы получаете

ServerName  ServiceName Date
SQL3    Service1    2018-06-07 00:00:00.000
SQL4    Service2    2018-06-07 00:00:00.000
SQL2    Service1    2018-06-07 00:00:00.000
SQL1    Service1    2018-06-07 00:00:00.000

С @Date = NULL и @ServerName = 'SQL1', вы получаете

ServerName  ServiceName Date
SQL1    Service1    2018-06-07 00:00:00.000
SQL1    Service2    2018-06-06 00:00:00.000

А с @Date = '20180607' и @ServerName = 'SQL1' вы получаете

ServerName  ServiceName Date
SQL1    Service1    2018-06-07 00:00:00.000
0 голосов
/ 07 июня 2018

Вам нужен динамический SQL-запрос или оператор подготовки!Мы можем создать оптимизирующую логику фильтрации на лету, объединив их обоих.Также можно изменить, чтобы ввести хранимую процедуру в том же ключе.

Оператор динамической подготовки: Создает подготовленную версию команды на экземпляре SQL Server с динамической фильтрацией.

  1. Подготовленный оператор не вводит SQL-инъекцию
  2. Улучшенное решение с логикой «Ниже» для динамического добавления параметров и фильтрации строк.(Со всеми параметрами в WHERE предложение приводит к сканированию таблицы, поскольку механизм выполнения готовится к худшему)

В следующем примере демонстрируется использование метода Prepare.

private static void SqlCommandPrepareEx(string connectionString)
{
    var sqlParameters = new List<SqlParameter>();
    var filterSb = new StringBuilder();

    PopulateParameters(sqlParameters, filterSb);

    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        var command = new SqlCommand(null, connection);

        // Create and prepare an SQL statement.
        command.CommandText = "SELECT ServerName, ServiceName, Status, Date" + "FROM ServicesStatus" + filterSb;

        // Adding Parameters
        command.Parameters.AddRange(sqlParameters.ToArray());
        // Call Prepare after setting the Command text and Parameters.
        command.Prepare();
        command.ExecuteNonQuery();
    }
}

private static void PopulateParameters(List<SqlParameter> sqlParameters, StringBuilder filterSb)
{
    if (!string.IsNullOrEmpty(InputDate))
    {
        // Add Parameter
        var sqlParameter = new SqlParameter("@date", SqlDbType.Date) { Value = InputDate };
        sqlParameters.Add(sqlParameter);
        // Add Filter
        filterSb.AppendLine(filterSb.Length > 0 ? " AND Date=@date" : " WHERE Date=@date");
    }

    if (!string.IsNullOrEmpty(StartDate))
    {
        // Add Parameter
        var sqlParameter = new SqlParameter("@startDate", SqlDbType.Date) { Value = StartDate };
        sqlParameters.Add(sqlParameter);
        // Add Filter
        // Using Between affects performance and don't allow indexes to be used
        filterSb.AppendLine(filterSb.Length > 0 ? " AND Date>=@startDate" : " WHERE Date>=@startDate");
    }

    if (!string.IsNullOrEmpty(EndDate))
    {
        // Add Parameter
        var sqlParameter = new SqlParameter("@endDate", SqlDbType.Date) { Value = EndDate };
        sqlParameters.Add(sqlParameter);
        // Add Filter
        // Using Between affects performance and don't allow indexes to be used
        filterSb.AppendLine(filterSb.Length > 0 ? " AND Date<=@endDate" : " WHERE Date<=@endDate");
    }

    if (!string.IsNullOrEmpty(ServerName))
    {
        // Add Parameter
        var sqlParameter = new SqlParameter("@serverName", SqlDbType.VarChar) { Value = ServerName };
        sqlParameters.Add(sqlParameter);
        // Add Filter
        filterSb.AppendLine(filterSb.Length > 0 ? " AND ServerName=@serverName" : " WHERE ServerName=@serverName");
    }
}

Понимание только динамических запросов: В следующем примере для понимания динамических запросов его можно сделать более безопасным, если ввести его в хранимую процедуру с соответствующей логикой.

var filterString = string.Empty;
string dynamicQuery = @"
SELECT        ServerName, ServiceName, Status, Date
FROM          ServicesStatus
{0}";

//Populate Filter String
var filterSb = new StringBuilder();

if(string.IsNullOrEmpty(dateFilter)){
    filterSb.AppendLine("Date = {0}", dateFilter)
}

// use similar code for other filters

if(filterSb.Length > 0){
    filterString = string.Format(" WHERE {0}", filterSb);
}

// Append filter
string.Format(dynamicQuery, filterString);

ПониманиеТолько подготовленные операторы: Создает подготовленную версию команды для экземпляра SQL Server.

private static void SqlCommandPrepareEx(string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        SqlCommand command = new SqlCommand(null, connection);

        // Create and prepare an SQL statement.
        command.CommandText =
            "INSERT INTO Region (RegionID, RegionDescription) " +
            "VALUES (@id, @desc)";
        SqlParameter idParam = new SqlParameter("@id", SqlDbType.Int, 0);
        SqlParameter descParam = 
            new SqlParameter("@desc", SqlDbType.Text, 100);
        idParam.Value = 20;
        descParam.Value = "First Region";
        command.Parameters.Add(idParam);
        command.Parameters.Add(descParam);

        // Call Prepare after setting the Commandtext and Parameters.
        command.Prepare();
        command.ExecuteNonQuery();

        // Change parameter values and call ExecuteNonQuery.
        command.Parameters[0].Value = 21;
        command.Parameters[1].Value = "Second Region";
        command.ExecuteNonQuery();
    }
}
...