【问题标题】:Run SQL Server function against each row in table对表中的每一行运行 SQL Server 函数
【发布时间】:2014-02-04 17:36:46
【问题描述】:

我有一个函数,它在表中“针对每一行”进行迭代。当它运行时,它应该查看每一行,为该行提取相关的 SET 值并运行该函数,该函数依次返回结果并使用正确的值更新正确的行。正在发生的事情是它正在运行并返回最后一行的值并使用该值更新所有行。任何想法我做错了什么?

SELECT 
     RowNum = ROW_NUMBER() OVER(ORDER BY ID)
     ,*
INTO #Geo
FROM DDDG

DECLARE @MaxRownum INT
SET @MaxRownum = (SELECT MAX(RowNum) FROM #Geo)

DECLARE @Iter INT
SET @Iter = (SELECT MIN(RowNum) FROM #Geo)

WHILE @Iter <= @MaxRownum
BEGIN
     SELECT *
     FROM #Geo
     WHERE RowNum = @Iter

    DECLARE @address nvarchar (100);
    Select @address =  Ad1 from DDDG where id = @Iter;

    DECLARE @state nvarchar (100);
    SET @state = 'FL';

    DECLARE @zip nvarchar (100);
    SET @zip = '33142';

    DECLARE @city nvarchar (100);
    SET @city = 'Miami';

    DECLARE @nation nvarchar (2);
    SET @nation = 'us';

    DECLARE @g geography;

    WAITFOR DELAY '00:00:00.050'
    SET @g = dbo.Geocode(@nation, @state, @city, @zip, @address);

    Update DDDG 
    Set Lat = @g.Lat 
    where id = @Iter;

    Update DDDG 
    Set long = @g.Long 
    where id = @Iter;

     SET @Iter = @Iter + 1
END

DROP TABLE #Geo

更新.....

谢谢大家......我几乎让它工作了......经过大量的试验和错误后,我发现我需要更改以下内容

Update DDDG Set Lat = @g.Lat where id = id;
Update DDDG Set long = @g.Long where id = id;

我现在只有一个问题。当我删除行时,RowNum 和 @Iter 不再匹配。除了每次都重新创建表格之外,有没有办法解决这个问题?

【问题讨论】:

  • 您应该重新编写逻辑以消除循环...SQL 是一种基于集合的语言,并且像这样执行得非常糟糕。当您放松线性思路时,它处理得更好,一个脚本来更新所有行,没有循环。
  • 您确定地理编码功能不只是为每个地址返回相同的纬度和经度吗?
  • 我敢肯定(威利博士的问题),至于更好的方法,我只做了 6 个月左右.. 如果你能告诉我一个更好的方法....我都是为它

标签: sql sql-server database tsql


【解决方案1】:

我可能遗漏了一些东西,但你为什么需要一个循环呢?:

UPDATE DDDG
SET Lat     = dbo.GeoCode('us','FL','Miami','33142',Ad1).Lat,
    Long    = dbo.GeoCode('us','FL','Miami','33142',Ad1).Long;

还可以通过将dbo.GeoCode 的调用次数减少到每行一个来进一步改进查询:

UPDATE t
SET Lat     = g.GC.Lat,
    Long    = g.GC.Long
FROM DDDG AS t
CROSS APPLY (
    SELECT dbo.GeoCode('us','FL','Miami','33142',Ad1) AS GC
) AS g;

【讨论】:

  • 我试过了,但是当以这种方式运行时,它会使用表中的最后一个地址更新所有记录
  • @user3266908 好吧,您并没有指定一种方法来知道哪一行是 last 地址
  • 此查询将更新整个表,因为缺少 WHERE 条件,并且所有记录中只会更新最后一个 Ad1 的 lat 和 long。
  • @DipenduPaul 问题中没有WHERE 条件,并且 op 正在循环整个表格
  • user3266908 在查询中使用了where id = @Iter
【解决方案2】:
WITH CTE_DDD(ID,Ad1)
AS
(
  SELECT ID, Ad1 FROM DDD
)
UPDATE d 
SET d.lat = dbo.Geocode(@nation, @state, @city, @zip,Ad1).lat, long = dbo.Geocode(@nation, @state, @city, @zip,Ad1).long 
FROM DDD d 
WHERE d.id = ID

在收到同行对上述问题的反对票后修改查询:

WITH CTE_DDD AS
(
  SELECT * FROM DDD
)
UPDATE CTE_DDD SET lat = (SELECT lat from dbo.Geocode(@nation, @state, @city, @zip,Ad)), long = (SELECT long from    dbo.Geocode(@nation, @state, @city, @zip,Ad))

我同意 CROSS APPLY 是一个更好的解决方案,因为它每行调用一次函数。

【讨论】:

  • 天啊,你最伟大。这是我一生中见过的最棒的代码.. 我会给你 5 颗星,我可以...这结束了长达一周的搜索....我对这一切都是新手,您向菜鸟或书籍推荐的任何网站?
  • 感谢并很高兴它有所帮助! W3Schools 网站是学习基础知识的好网站;当然,您会在 Internet 上获得由 SQL 大师撰写的精彩文章。从加入、选择、插入、删除、更新、分组、CTE、row_number() 开始...我最近发现在这样的论坛上活跃是一种很好的学习方式,所以我也向你推荐它。
  • 抱歉,这到底是如何工作的?您没有为Ad1 提供表别名,因此它应该会报错。为什么你需要自己做一个JOIN,更新表格要简单得多
  • @Lamak,他没有将表格加入到自身中(除非答案已被编辑)。他正在定义一个根本不使用的 CTE。而WHERE d.id = ID 完全是多余的......
  • @ypercube 你是对的,当然。 id = ID 把我吓跑了,我以为是JOIN
猜你喜欢
  • 2017-04-08
  • 2017-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多