使用用户定义的函数动态更新临时表和数据库表条目

Posted

技术标签:

【中文标题】使用用户定义的函数动态更新临时表和数据库表条目【英文标题】:Using user-defined functions to dynamically update temp tables and database table entries 【发布时间】:2016-01-14 20:43:23 【问题描述】:

我需要做的是更改 SQL 中的 ActivePath 条目,该条目的值和长度都更改为不同值和长度的不同路径,然后一遍又一遍地运行它,直到没有更多与要更改的 ActivePath 匹配的条目.

这是我目前所拥有的,它适用于我已经知道当前文件路径、新文件路径和文件名的单个文件:

UPDATE [AuroraFileServer].[dbo].[File]
SET ActivePath = REPLACE(ActivePath, 'C:\ProgramData\MyData\FileServer\Data', 'C:\Videos\Archive 1')
WHERE ActivePath IN (SELECT ActivePath FROM [AuroraCore].[dbo].[DeviceEventFile] AS DEF
    JOIN [AuroraCore].[dbo].[DeviceEvent] AS DE
    ON DE.Id = DEF.DeviceEventId 
    JOIN [AuroraFileServer].[dbo].[File] AS F
    ON DEF.FilePath = F.ActivePath
    WHERE DE.Name LIKE 'ACBD13420160111185621001%')

所以概念证明有效,但我需要它更加动态,因为对数百或可能数千个视频运行它是不切实际的。

旧的 ActivePath 位置会根据视频上传到系统的日期在 yyyy/mm/dd 基础上发生变化,其中月份和日期可以是一位或两位数字值,具体取决于月份或日期(1/1 与12/12、1 月 1 日和 12 月 12 日)。新的 ActivePath 需要位于不同的位置,但旧的 ActivePath yyyy/mm/dd + 1 天作为文件存档从主要位置到存档仅 24 小时后。

所以流程需要这样取:

C:\ProgramData\MyData\FileServer\Data\2016\1\13\ACBD13420160111185621001i100.avi

并将其更改为:

C:\Videos\Archive 1\2016\1\14\ACBD13420160111185621001i100.avi

对于数据库中的数百或数千个条目,当然,在文件实际名称更改之前几乎所有内容都发生了变化。

有没有办法让它与创建一个表或索引一起工作,其中所有 ActivePath 条目都可以转储到与 C:\ProgramData... 路径匹配的位置,并使用 a 对该文件运行查询replace 语句使用索引中的单行作为要替换的部分,替换它,然后一遍又一遍地重复遍历列表,直到所有条目都被替换?我已经看到其他遵循这个想法的替换语句,但是所有新旧变量都是已知的,这不是我的情况。

我认为我可以跑:

SELECT ActivePath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

然后将结果返回到包含所有内容的表或索引中,在我的情况下(这将在我的测试环境之外更改),在第 8 个 '\' 被截断并删除重复项以获取列表中的每一行将是 REPLACE 语句的第一个 ' ' 中的单独条目。 REPLACE 语句的第二部分需要复制原始路径的日期部分 + 1 天(因此 31 +1 需要更改月份 +1 以保持日期的工作方式)。这获得了我们运行 REPLACE 语句所需的信息,该语句将循环回到开头并重复,直到该表或索引中的所有行都已完成然后停止。我只是不知道如何做到这一点或从哪里开始。

编辑:

因此,通过使用 bdn02 中的功能,我已经更接近我需要的功能了。这是我目前所拥有的:

(
@olddir varchar(300)
)
RETURNS  varchar(300) AS
BEGIN 
declare @tmpvar varchar(200)
declare @index int
declare @year varchar(4)
declare @month varchar(2)
declare @day varchar(2)
declare @filename varchar(200)
declare @videodate datetime
declare @newpath varchar(300)
set @tmpvar = replace(@olddir, 'C:\ProgramData\MyData\FileServer\Data\', '')
set @index = charindex('\', @tmpvar)
set @year = substring(@tmpvar, 1, @index-1)
set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @index = charindex('\', @tmpvar)
set @month = substring(@tmpvar, 1, @index-1)
set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @index = charindex('\', @tmpvar)
set @day = substring(@tmpvar, 1, @index-1)
set @filename = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @videodate = CONVERT (datetime, @day + '.' + @month + '.' + @year, 104)
set @videodate = DATEADD (day , 1 , @videodate)
--build new path
set @newpath = 'C:\Videos\Archive 1\' + cast(year(@videodate) as varchar) + '\' + cast(month(@videodate) as varchar) + '\' + cast(day(@videodate) as varchar) + '\'
return @newpath
END

使用时返回新路径:

SELECT DISTINCT dbo.ConvertDir(ActivePath)
FROM AuroraFileServer.dbo.[File] 
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%' 

好的,现在:

(
@olddir varchar(300)
)
RETURNS  varchar(300) AS
BEGIN 
declare @tmpvar varchar(200)
declare @index int
declare @year varchar(4)
declare @month varchar(2)
declare @day varchar(2)
declare @filename varchar(200)
declare @videodate datetime
declare @oldpath varchar(300)
set @tmpvar = replace(@olddir, 'C:\ProgramData\MyData\FileServer\Data\', '')
set @index = charindex('\', @tmpvar)
set @year = substring(@tmpvar, 1, @index-1)
set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @index = charindex('\', @tmpvar)
set @month = substring(@tmpvar, 1, @index-1)
set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @index = charindex('\', @tmpvar)
set @day = substring(@tmpvar, 1, @index-1)
set @filename = substring(@tmpvar, @index+1, len(@tmpvar)-@index)
set @videodate = CONVERT (datetime, @day + '.' + @month + '.' + @year, 104)
--build new path
set @oldpath = 'C:\ProgramData\MyData\FileServer\Data\' + cast(year(@videodate) as varchar) + '\' + cast(month(@videodate) as varchar) + '\' + cast(day(@videodate) as varchar) + '\'
return @oldpath
END

使用时返回旧路径:

SELECT DISTINCT dbo.ConvertDir1(ActivePath)
FROM AuroraFileServer.dbo.[File] 
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

完美,所以现在我有了没有重复的旧路径和新路径。

现在我正在尝试利用一个新函数将两个 udf 放在一个带有 WHERE 循环的替换语句中。问题是我得到“找不到列“dbo”或用户定义的函数或聚合“dbo.ConvertDir”,或者名称不明确。在我的 dbo.ConvertDir 和 dbo.ConvertDir1 的新函数中。我的默认架构是 dbo。这是函数:

DECLARE @oldpath TABLE (old varchar(255))
DECLARE @newpath TABLE (new varchar(255))

INSERT INTO @oldpath (OLD)
SELECT DISTINCT dbo.ConvertDir1(oldpath);

INSERT INTO @newpath (NEW)
SELECT DISTINCT dbo.ConvertDir(newpath);

WHILE (1=1)

BEGIN
    UPDATE f
    SET    f.ActivePath = REPLACE(f.ActivePath, o.old, n.new)
    FROM   AuroraFileServer.dbo.[File] AS f,
           @oldpath AS o,
           @newpath AS n
    WHERE f.ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

    IF @@ROWCOUNT = 0
      BREAK
END

SELECT * FROM AuroraFileServer.dbo.[File]

我做错了什么?

【问题讨论】:

我觉得写一个特定的函数来获取旧路径作为输入,解析它,添加一天并返回新路径更简单。您可以直接在选择中使用它 我可能需要进一步解释,但让我对这种方法进行一些研究,看看我想出了什么。 【参考方案1】:

我写了一个函数,似乎可行。也许并不完美....

CREATE FUNCTION ConvertDir ( @olddir varchar(300) ) RETURNS varchar(300) AS BEGIN declare @tmpvar varchar(200) declare @index int declare @year varchar(4) declare @month varchar(2) declare @day varchar(2) declare @filename varchar(200) declare @videodate datetime declare @newpath varchar(300) set @tmpvar = replace(@olddir, 'C:\ProgramData\MyData\FileServer\Data\', '') set @index = charindex('\', @tmpvar) set @year = substring(@tmpvar, 1, @index-1) set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index) set @index = charindex('\', @tmpvar) set @month = substring(@tmpvar, 1, @index-1) set @tmpvar = substring(@tmpvar, @index+1, len(@tmpvar)-@index) set @index = charindex('\', @tmpvar) set @day = substring(@tmpvar, 1, @index-1) set @filename = substring(@tmpvar, @index+1, len(@tmpvar)-@index) set @videodate = CONVERT (datetime, @day + '.' + @month + '.' + @year, 104) set @videodate = DATEADD (day , 1 , @videodate) --build new path set @newpath = 'C:\Videos\Archive 1\' + cast(year(@videodate) as varchar) + '\' + cast(month(@videodate) as varchar) + '\' + cast(day(@videodate) as varchar) + '\' + @filename return @newpath END

调用它: select dbo.ConvertDir('C:\ProgramData\MyData\FileServer\Data\2016\1\13\ACBD13420160111185621001i100.avi')

您可以在选择或视图中使用该功能

【讨论】:

好的,这样肯定会为单个文件路径返回正确的新路径。现在,我试图弄清楚如何针对文件表中具有“C:\ ProgramData \ ...”的所有不同文件路径运行该文件,而无需手动查找每个文件路径并将其输入查询然后在再次重复该过程之前构建一个 REPLACE 语句。知道该怎么做吗? SELECT ActivePath, dbo.ConvertDir(ActivePath) FROM AuroraFileServer.dbo.[File] WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%' 好的,所以修改上述函数以返回没有文件名的旧路径并使用查询:SELECT DISTINCT dbo.ConvertDir(ActivePath), dbo.ConvertDir1(ActivePath) FROM AuroraFileServer.dbo .[File] WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%' 为我提供了新旧路径,因为它们是 REPLACE 语句所需要的,没有重复。现在,我将如何使用该查询将结果放入 REPLACE 语句中? THIS 页面上的想法在这里适用吗?【参考方案2】:

好的,我想通了!

INSERT INTO @oldpath (OLD)
SELECT DISTINCT dbo.ConvertDir1(oldpath);

上面缺少 FROM 和 WHERE,也没有优化,因此选择 TOP 1 而不是 SELECT DISTINCT。这正是它所需要的:

INSERT INTO @oldpath (OldPath)
SELECT TOP 1 dbo.ConvertDir1(ActivePath) AS OldPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

现在,UPDATE f 块很好,但是为了循环和更新每个具有与上述 INSERT INTO 匹配的路径的条目,这些临时表中的每一个都必须在每次传递时更新。这就是我想出的办法:

UPDATE @oldpath
SET OldPath = (SELECT TOP 1 dbo.ConvertDir1(ActivePath) AS OldPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%')

所以它的工作方式,这就是我的设想,是循环将用一行更新临时表以查找/替换路径,更新 dbo.file 表,然后循环返回用下一个路径更新临时表以查找/替换,然后一遍又一遍地重复,直到 UPDATE f 块不再更新任何条目。一旦我开始工作,我就把它移植过来更新一个非常相似的表。

这是完整的查询:

DECLARE @oldpath TABLE (OldPath varchar(255))
DECLARE @newpath TABLE (NewPath varchar(255))
DECLARE @oldpath2 TABLE (OldPath2 varchar(255))
DECLARE @newpath2 TABLE (NewPath2 varchar(255))

INSERT INTO @oldpath (OldPath)
SELECT TOP 1 dbo.ConvertDir1(ActivePath) AS OldPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

INSERT INTO @newpath (NewPath)
SELECT TOP 1 dbo.ConvertDir(ActivePath) AS NewPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

INSERT INTO @oldpath2 (OldPath2)
SELECT TOP 1 dbo.ConvertDir1(FilePath) AS OldPath2
FROM AuroraCore.dbo.DeviceEventFile
WHERE FilePath LIKE 'C:\ProgramData\MyData\FileServer%'

INSERT INTO @newpath2 (NewPath2)
SELECT TOP 1 dbo.ConvertDir(FilePath) AS NewPath2
FROM AuroraCore.dbo.DeviceEventFile
WHERE FilePath LIKE 'C:\ProgramData\MyData\FileServer%'

WHILE (1=1)

BEGIN

UPDATE @oldpath
SET OldPath = (SELECT TOP 1 dbo.ConvertDir1(ActivePath) AS OldPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%')

UPDATE @newpath
SET newpath = (SELECT TOP 1 dbo.ConvertDir(ActivePath) AS NewPath
FROM AuroraFileServer.dbo.[File]
WHERE ActivePath LIKE 'C:\ProgramData\MyData\FileServer%')

UPDATE @oldpath2
SET oldpath2 = (SELECT TOP 1 dbo.ConvertDir1(FilePath) AS OldPath2
FROM AuroraCore.dbo.DeviceEventFile
WHERE FilePath LIKE 'C:\ProgramData\MyData\FileServer%')

UPDATE  @newpath2
SET newpath2 = (SELECT TOP 1 dbo.ConvertDir(FilePath) AS NewPath2
FROM AuroraCore.dbo.DeviceEventFile
WHERE FilePath LIKE 'C:\ProgramData\MyData\FileServer%')

UPDATE f
    SET    f.ActivePath = REPLACE(f.ActivePath, o.OldPath, n.NewPath)
    FROM   AuroraFileServer.dbo.[File] AS f,
           @oldpath AS o,
           @newpath AS n
    WHERE f.ActivePath LIKE 'C:\ProgramData\MyData\FileServer%'

UPDATE def
    SET    def.FilePath = REPLACE(def.FilePath, o2.OldPath2, n2.NewPath2)
    FROM   AuroraCore.dbo.DeviceEventFile AS def,
           @oldpath2 AS o2,
           @newpath2 AS n2
    WHERE def.FilePath LIKE 'C:\ProgramData\MyData\FileServer%'

    IF @@ROWCOUNT = 0
      BREAK
        ELSE
          CONTINUE
END

感谢@bdn02 为我提供了第一个功能!

【讨论】:

以上是关于使用用户定义的函数动态更新临时表和数据库表条目的主要内容,如果未能解决你的问题,请参考以下文章

无法从 plpgsql 函数中的动态命名临时表运行“选择进入”

MySQL 存储函数 - 动态/变量表和列名

带有表变量的动态查询以循环遍历所有表以在数据库中更新

SQLSERVER Tempdb的作用及优化

如何将ACCESS的表和查询创建成动态的WEB?

让函数返回表的更新部分