【问题标题】:Query with a lot of joins - porting C# to SQL Server使用大量连接进行查询 - 将 C# 移植到 SQL Server
【发布时间】:2024-04-25 04:40:02
【问题描述】:

我正在尝试将一些 C# 移植到 TSQL,因为性能不能令人满意。 C# 进行多个数据库调用。

这是用于住宿属性的刮板。架构如下:

刮擦

  • ScrapeId
  • fkProviderId
  • fkLocation
  • fkSessionId
  • 开始
  • 结束

ScrapeResults

  • fkScrapeId
  • 价格
  • fkRoomId

本质上,这一切的重点是数据库跟踪内部房间价格与竞争对手附近的比较。 ScrapeResults 上的 fkRoomId 列唯一地定义了每个房间。每次抓取竞争对手时,Scrapes 表中都会有一个新条目,并且抓取的结果保存在 ScrapeResults 中。开始和结束日期为 1 周。刮擦每周价格为 3 个月。抓取每周发生一次,这是 fkSessionId 的目的,它与独特的抓取会话有关。

C# 代码构建的报告如下所示:

  • 开始日期
  • 结束日期
  • 我们的房型
  • 竞赛室1
  • 竞赛室2
  • ...
  • 比赛室n

我不太确定是否可以为此构建 SQL 查询。我编写了一个构建动态 SQL 字符串的存储过程。这是我的第一次尝试:

select sr.ScrapeId,
    sr.fkProviderId,
    sr.startDate,
    sr.endDate,
    sr1.price,
    sr2.price,
    sr3.price,
    sr4.price,
    sr5.price,
    sr6.price,
    sr7.price,
    sr8.price,
    sr9.price,
    sr10.price,
    sr11.price,
    sr12.price 
    from Scrapes sr 
    left join ScrapeResults sr1 on sr1.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr2 on sr2.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr3 on sr3.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr4 on sr4.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr5 on sr5.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr6 on sr6.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr7 on sr7.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr8 on sr8.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr9 on sr9.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr10 on sr10.fkScrapeId = sr.ScrapeId 
    left join ScrapeResults sr11 on sr11.fkScrapeId = sr.ScrapeId
    left join ScrapeResults sr12 on sr12.fkScrapeId = sr.ScrapeId 
    WHERE sr.fkSession = 25
    and sr.startDate='2014-03-22'
    and sr.fkLocationId = 1 
    and sr1.fkRoomId = 11 
    and sr2.fkRoomId = 15 
    and sr3.fkRoomId = 19 
    and sr4.fkRoomId = 23 
    and sr5.fkRoomId = 25 
    and sr6.fkRoomId = 27 
    and sr7.fkRoomId = 32 
    and sr8.fkRoomId = 39 
    and sr9.fkRoomId = 41 
    and sr10.fkRoomId = 45
    and sr11.fkRoomId = 47 
    and sr12.fkRoomId = 50

我知道我在这里不合时宜,但希望得到指点。谢谢。

【问题讨论】:

  • 您是从 LINQ 移植回 T-SQL 吗?您能否向我们展示您进行“多次数据库调用”的代码?我在处理具有许多连接甚至动态构造的查询的复杂查询方面拥有丰富的经验,它们的运行速度与存储过程一样快。我也许可以建议您如何改进现有代码而不是重写它

标签: sql sql-server performance tsql


【解决方案1】:

您通过 srX.fkRoomId =
破坏了左连接 所以还不如做一个加入
或者在这种格式中,您实际上可以进行左连接而不破坏它
查询优化器通常可以更好地使用语法
我认为在 C# 中构建它没有问题

  select sr.ScrapeId,
         sr.fkProviderId,
         sr.startDate,
         sr.endDate,
         sr1.price,
         sr2.price,
         ...
    from Scrapes sr 
    join ScrapeResults sr1 
      on sr1.fkScrapeId = sr.ScrapeId 
     and sr1.fkRoomId = 11
    join ScrapeResults sr2 
      on sr2.fkScrapeId = sr.ScrapeId 
     and sr2.fkRoomId = 15
    ....
   WHERE sr.fkSession = 25
     and sr.startDate='2014-03-22'
     and sr.fkLocationId = 1 

【讨论】:

  • 谢谢,这已经解决了眼前的问题并使选择工作正常。
【解决方案2】:

您可以使用 PIVOT 选项更简单地执行此操作,以 fkRoomID 列为轴。您可以使用单个查询手动执行此操作,但这让我很恼火,因为您必须在 SQL 中为 PIVOT 语句指定 fkRoomID 值,因此如果房间数量发生变化,您必须更改 SQL,如下所示:

select 
   sc.ScrapeId,
   sc.fkProviderId,
   sc.startDate,
   sc.endDate,
   scr.fkRoomId,
   scr.price
from 
   Scrapes sc

   join ScrapeResults scr
      on ( scr.fkScrapeId = sr.ScrapeId )
pivot (max(price) for fkRoomId in ([11],[15],[19],[23],[25],[27],[32],[39],[41],[45],[47],[50]))
where
   sr.fkSession = 25
   and sr.startDate = '2014-03-22'
   and sr.fkLocationId = 1

我更喜欢使用我编写的名为 pivot_query 的存储过程。要使用它,您可以像这样格式化查询:

declare @mySQL varchar(MAX);

set @mySQL = '
select 
   sc.ScrapeId,
   sc.fkProviderId,
   sc.startDate,
   sc.endDate,
   scr.fkRoomId,
   scr.price
from 
   Scrapes sc

   join ScrapeResults scr
      on ( scr.fkScrapeId = sr.ScrapeId )
where
   sr.fkSession = 25
   and sr.startDate = ''2014-03-22''
   and sr.fkLocationId = 1
';

exec pivot_query @mySQL, 'StartDate, EndDate, fkProviderId','fkRoomId','max(price)'

这是基本概念,但如果您有另一个包含房间名称的参考表,您也可以加入该表并使用房间名称进行透视,因此列将房间名称放在顶部。

有一些使用 pivot_query proc here 的示例。

【讨论】:

  • 嗨@Ron Savage,谢谢,我认为支点绝对是我想要走的路线。虽然我有一些错误 - 一个是两个表都有一个 createStamp。我收到错误The column 'createStamp' was specified multiple times for 'pvt'.(将as pvt 附加到数据透视块的末尾。对于每一列,例如The multi-part identifier "sr.fkSession" could not be bound.,我也得到了 - 所有列都会发生这种情况。
  • 嗨@Sam,如果您同时包含表中的两个 createStamp 字段,请将其中一个别名为不同的名称,例如 scr_createStamp,以便列名仍然是唯一的 - 试一试!
  • 嗨@Ron。我没有将它们包括在选择中 - 但无论如何它仍然失败。
最近更新 更多