[英]c# vs mysql: calling function in sql select statement vs fetching data and calling same function in c#
我们是一个有多个产品保证的产品网站。 保证仅适用于少数具有特定交易商编号的产品。 这两个表是:
具有ID,名称,cityId,dealerId,价格列的产品表。 该表包含所有产品。
列为DealerId的GuaranteeDealers表。 这为所有经销商提供了有保证的产品。
我们希望获得所有产品的信息,无论是否保证。 查询如下:
方法1:将isGuaranteed从sql函数获取到服务器(c#)端:
从客户中选择ID,名称,cityId,dealerId,价格,isGuaranteed = isGuaranteed(DealerId)
isGuaranteed是一个SQL函数,用于检查guranteeDealers表中是否有DealerId。 如果是,则返回1,否则返回0。
我有50000个产品和500个这样的经销商,此查询执行时间太长。
要么
方法2:获取经销商列表,并在c#(服务器)端设置isGuaranteed标志。
选择ID,名称,cityId,dealerId,价格。 将这些映射到C#产品列表
从guaranteeDealers表中选择DealerId到c#经销商列表中。
在c#中迭代产品记录,并通过c#函数设置isGuaranteed标志,该标志检查产品的DealerId是否在guaranteeDealers的c#列表中。
相较于1。
虽然这两种方法看起来与我相似,但是有人可以解释为什么在MySQL的select语句中执行函数需要这么长时间吗? 还是正确的做法,方法1或2?
问:“为什么在MySQL的select语句中执行功能需要这么长时间?”
在性能方面,执行相关子查询50,000次将吃掉我们的午餐,而如果我们不小心的话,它也会吃掉我们的午餐盒。
该子查询将针对外部查询返回的每一行执行。 这就像执行50,000个单独的SELECT语句一样。 这将需要时间。
将相关子查询隐藏在MySQL存储程序(函数)中无济于事。 这只会增加子查询每次执行的开销,并使事情变慢。 如果我们删除函数并内联该子查询,那么我们可能正在寻找这样的东西:
SELECT p.id
, p.name
, p.cityId
, p.dealerId
, p.price
, IFNULL( ( SELECT 1
FROM guaranteeDealers d
WHERE d.dealerId = p.dealerID
LIMIT 1
)
,0) AS isGuarantee
FROM products p
ORDER BY ...
对于从products
返回的每一行(没有被谓词(例如WHERE子句中的条件)过滤掉),这实际上是在告诉MySQL执行单独的 SELECT语句。 运行查询以查看是否在guaranteeDealers
表中找到了dealerID
。 每一行都会发生这种情况。
如果外部查询仅返回几行,那么这仅是执行几个额外的SELECT语句,并且我们实际上不会注意到额外的时间。 但是,当我们返回数以万计的行时,它开始累加起来。 就所有这些查询执行所花费的总时间而言,它变得昂贵。
而且,如果我们将“子”查询“隐藏”在MySQL存储程序(函数)中,则会增加更多开销,并引入大量上下文切换。 从在数据库上下文中执行查询开始,调用一个函数,该函数切换到执行该功能的存储程序引擎,然后该引擎需要运行数据库查询,然后又切换回数据库上下文以执行查询并返回结果集。返回到存储程序环境以处理结果集并返回值,然后切换回原始数据库上下文以获取返回的值。 如果我们必须做几次,那就没问题了。 重复数万次,这将增加开销。
(请注意,本机MySQL内置函数没有相同的上下文切换开销。本机函数是在数据库上下文中执行的编译代码。这是我们偏爱本机函数而不是MySQL存储程序的一个重要原因。)
如果我们想提高性能,则需要放弃处理RBAR(行排成行),这对于大型集来说变得非常慢。 我们需要按组而不是按行处理问题。
我们可以告诉MySQL返回什么集合 ,然后让它找出最有效的返回方法。 而不是我们轮跳闸来回数据库,执行单个SQL语句零碎获得一套小位,使用MySQL的决定应当如何准备集中的指令。
在回答问题
问:“哪种方法正确?”
两种方法都是“正确的”,就像它们返回我们要的集合一样。
第二种方法“更好”,因为它大大减少了需要执行的SELECT语句的数量(2条语句而不是50,001条语句)。
就最佳方法而言,通常最好让MySQL执行行的“匹配”,而不是在客户端代码中进行匹配。 (为什么不必要地使我们的代码杂乱无章地执行通常可以在数据库中更高效地完成的操作。)是的,有时我们需要在代码中进行匹配。 有时候结果会更快。
但是有时候,我们只能编写一个 SELECT语句来指定要返回的集合 ,然后让MySQL继续使用。 如果速度很慢,我们可以做一些调整,查看执行计划,确保有合适的索引可用,并调整查询。
给定问题中有关要返回的集合的信息,并假定dealerId
在guaranteeDealers
表中是唯一的。 如果我们的“测试”是guaranteeDealers
表中是否存在匹配的行,则可以使用OUTER JOIN操作,并且SELECT列表中的表达式返回0或1,具体取决于是否找到匹配的行。
SELECT p.id
, p.name
, p.cityId
, p.dealerId
, p.price
, IF(d.dealerId IS NULL,0,1) AS isGuarantee
FROM products p
LEFT
JOIN guaranteeDealers d
ON d.dealerId = p.dealerId
ORDER BY ...
为了获得最佳性能,我们将需要定义合适的索引。 最少(如果尚未定义这样的索引)
ON guaranteeDealers (dealerId)
如果还有其他涉及产生结果的表,那么我们希望将该表也包含在执行的查询中。 这将使MySQL优化器有机会提出最有效的计划以返回整个集合。 并且不限制MySQL执行单个操作是零碎的返回位。
方法1是更好的方法,因为它减小了从数据库服务器传输到应用程序服务器的结果集的大小。 它的性能问题是由isGuaranteed
函数引起的,该函数每行( customers
表的行)执行一次,这似乎是一个错字。 这样的方法将更加高效:
select p.id, p.name, p.cityId, p.dealerId, p.price, gd.IsGuaranteed is not null
from Product p
left join GuaranteeDealers gd on p.dealerId = gd.dealerId
select id, name, cityId, customers.dealerId, price,
isGuaranteed = guaranteeDealers.dealerId is not null
from customers left join guaranteeDealers
on guaranteeDealers.dealerId = customets.dealerId
无需调用函数。
请注意,我曾经使用过客户,因为那是您在问题中使用的表格-尽管我怀疑您可能是指产品。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.