sql-server - 参数化存储过程条件连接,通过 3 个参数在连接表中搜索

标签 sql-server t-sql stored-procedures conditional-statements left-join

我希望我的存储过程具有条件联接,如果满足三个/或全部或两个条件之一,那么我们联接两个表(地址到订单)并根据搜索条件从订单表返回结果在地址表中。

这是我的存储过程:

USE [Test]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[OrderLoadAllPaged]
    @OrderId int = 0,
    @WarehouseId int = 0,
    @PaymentMethodSystemName nvarchar(max) = null,
    @OrderStatusId int = 0,
    @PaymentStatusId int = 0,
    @ShippingStatusId int = 0,
    @BillingEmail nvarchar(max) = null,
    @BillingFirstName nvarchar(max) = null,
    @BillingLastName nvarchar(max) = null,
    @ShippingMethod nvarchar(max) = null,
    @CreatedFromUtc datetime = null,
    @CreatedToUtc datetime = null
AS
BEGIN
    DECLARE
        @sql nvarchar(max)

    SET NOCOUNT ON;

        SELECT TOP 100 *
        FROM [Test].[dbo].[Order] o with (NOLOCK)

    LEFT join [Test].[dbo].[Address] a on (a.Id LIKE o.BillingAddressId)
    WHERE (@BillingEmail IS null OR a.[Email] LIKE '%@BillingEmail%') 
    AND (@BillingFirstName IS null OR a.[FirstName] LIKE '%@BillingFirstName%')
    AND (@BillingLastName IS null OR a.[LastName] LIKE '%@BillingLastName%')

    AND
        o.[Deleted] = 0

    AND (o.[Id] = @OrderId OR @OrderId = 0)
    AND (o.[WarehouseId] = @WarehouseId OR @WarehouseId = 0)

    AND (@PaymentMethodSystemName IS null OR o.[PaymentMethodSystemName] = @PaymentMethodSystemName)
    AND (o.[OrderStatusId] = @OrderStatusId OR @OrderStatusId = 0)
    AND (o.[PaymentStatusId] = @PaymentStatusId OR @PaymentStatusId = 0)

    AND (o.[ShippingStatusId] = @ShippingStatusId OR @ShippingStatusId = 0)

    AND (@ShippingMethod IS null OR o.[ShippingMethod] = @ShippingMethod)
    AND o.[CreatedOnUtc] >= ISNULL(@CreatedFromUtc, '1/1/1900')
    AND o.[CreatedOnUtc] < ISNULL(@CreatedToUtc, '1/1/2999')

    ORDER BY o.[CreatedOnUtc] DESC
END

这是我在锻炼时遇到的问题:

LEFT join [Test].[dbo].[Address] a on (a.Id LIKE o.BillingAddressId)
    WHERE (@BillingEmail IS null OR a.[Email] LIKE '%@BillingEmail%') 
    AND (@BillingFirstName IS null OR a.[FirstName] LIKE '%@BillingFirstName%')
    AND (@BillingLastName IS null OR a.[LastName] LIKE '%@BillingLastName%')

我感觉自己走在正确的轨道上,但遇到了某种超时错误

Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

这告诉我查询一定有问题。

如果我删除电子邮件、名字和姓氏参数,查询就会完成。

我可以确认这就是我希望的工作方式,因为我手动编写了查询,这就是我所需要的:

SELECT TOP 100 *
  FROM [Test].[dbo].[Order] o
  LEFT JOIN  [Test].[dbo].[Address] a
  ON o.[BillingAddressId] = a.Id
  WHERE a.[Email] LIKE '%chris1%' AND a.FirstName LIKE '%chris%' AND a.LastName LIKE '%c%'
  ORDER BY o.[CreatedOnUtc]  

我的参数以 C# 代码发送到数据库,如下所示:

var pBillingEmail = _dataProvider.GetParameter();
pBillingEmail.ParameterName = "BillingEmail";
pBillingEmail.Value = (!String.IsNullOrEmpty(billingEmail)) ? (object)billingEmail : DBNull.Value;
pBillingEmail.DbType = DbType.String;

var pBillingFirstName = _dataProvider.GetParameter();
pBillingFirstName.ParameterName = "BillingFirstName";
pBillingFirstName.Value = (!String.IsNullOrEmpty(billingFirstName)) ? (object)billingFirstName : DBNull.Value;
pBillingFirstName.DbType = DbType.String;

var pBillingLastName = _dataProvider.GetParameter();
pBillingLastName.ParameterName = "BillingLastName";
pBillingLastName.Value = (!String.IsNullOrEmpty(billingLastName)) ? (object)billingLastName : DBNull.Value;
pBillingLastName.DbType = DbType.String;

有人知道我哪里出了问题吗?

最佳答案

LIKE '%@BillingEmail%' 应该是 LIKE '%' + @BillingEmail + '%' 否则您正在查找包含字符串 '@BillingEmail' 而不是变量的内容。

不幸的是,当您尝试“包含”搜索时,例如like '%somesearchterm% SQL Server 无法使用索引来缩小搜索范围,因此必须读取并检查正在搜索的表中的每条记录。因此它会相当慢。

这种广义搜索很难很好地执行。然而,虽然拥有索引并不能缩小需要搜索的行的范围,但在 BillingEmail 上拥有索引确实可以减少 SQL Server 为了执行搜索而必须加载的数据量,并且可以使性能得到相当显着的提高。这也意味着在您搜索时表本身并未被锁定。

所以你可以考虑添加3个索引,BillingEmail、BillingFirstName、BillingLastName。您需要根据系统的工作方式对此进行自定义,但例如,如果人们通常搜索名字和姓氏,您可以在这两列上使用单个索引。

如果您想在许多列上进行搜索,这不太可扩展。您可以通过创建组合字段来扩展这个想法,例如FullName 在触发器上更新为 FirstName + ' ' + LastName,然后您可以索引 FullName

当您实际执行搜索时,通常可以更好地对一个搜索词进行第一次搜索并将结果(或仅 ID)存储在临时表中,然后针对临时表执行进一步的过滤操作。如果您可以让用户通过初始选择以某种方式缩小搜索范围,这尤其有用。

全文索引也是一种选择。

关于sql-server - 参数化存储过程条件连接,通过 3 个参数在连接表中搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59080065/

相关文章:

database - 如何使用较旧的日期字段删除 SQL 中的重复行

sql - 如何在不指定 column_name 的情况下更改所有列?

sql - 检查 SQL 中满足 10 个条件中的任意 4 个

sql-server - 使用 SQL Server 作为 Orleans 存储

SQL 分组结果不相等

Select 中的 MySQL 程序?

sql - 如何将波斯数字转换为 int

sql-server - 查找某个时间范围内的事件

sql - 如何以交叉的方式确定行之间的月份差异

stored-procedures - SQL Server 2012 存储过程被自动删除