如何替换Access SQL中的多个字符?

时间:2009-04-14 17:48:02

标签: sql vba ms-access replace access-vba

我是SQL的新手,所以希望有人可以为我拼出这个。我尝试按照“在SQL查询中替换多个字符串”的帖子,但我被卡住了。

我正在尝试做与上述发布的发起人相同的事情,但使用不同的表和不同的字段。假设表“ShiptoPlant”中的以下字段“BTST”有三条记录(我的表实际上有数千条记录)......

表名:BTST

   ---------------
   | ShiptoPlant |
   | ----------- |
   | Plant #1    |
   | Plant - 2   |
   | Plant/3     |
   ---------------

以下是我在SQL屏幕中输入的内容:

SELECT CASE WHEN ShipToPlant IN ("#", "-", "/") Then ""
ELSE ShipToPlant END FROM BTST;

我一直收到消息(错误3075)......

"Syntax error (missing operator) in query expression 
'CASE WHEN ShiptoPlant IN (";","/"," ") Then "" ELSE ShipToPlant END'."

我想对键盘上的每个字符执行此操作,但"*"除外,因为它是通配符。

非常感谢您提供的任何帮助!

编辑:从评论中添加的背景信息

我已经从我们的14个供应商处收集了2008日历年的行项目发票级数据。我正在尝试规范供应商提供给我们的工厂名称。

每个供应商可以使用不同的名称来呼叫工厂,例如

我们的主列表上的

Signode服务可以由供应商调用

Signode Service 
Signode - Service.
SignodeSvc
SignodeService

我正在尝试剥离非字母数字字符,以便我可以尝试使用我们的主列表来识别工厂,通过创建一系列链接来查看前10个字符,如果没有匹配,则8个字符,6个,4个...

我的基本挂断是我不知道如何从表中删除字母数字字符。我将在几个列上执行此操作,但我计划创建单独的查询以编辑其他列。

也许我需要做一个剥离所有字母数字的大规模更新查询。我还不清楚如何写它。这就是我开始采取的所有空间。它工作得很好,但是当我尝试嵌套替换

时失败了
UPDATE BTST SET ShipToPlant = replace(ShipToPlant," ","");

编辑2:从评论中获取更多信息

每个月,我们的订单项发票数据中都会显示最多100个新工厂名称的排列 - 这可能代表数千张发票记录。我正在尝试构建一种快速而又脏的方法,为每个工厂名称排列分配一个确定名称的master_id。我能看到的最好的方法是查看工厂,地址,城市和州字段,但问题是这些字段也有各种排列,例如,

128 Brookview Drive
128 Brookview Lane

取出字母数字并做

LEFT(PlantName,#chars) & _
LEFT(Address,#chars) & _
LEFT(City,#chars) & _
LEFT(State,#chars) 

并且通过更改字符数直到在发票数据和主工厂列表之间找到匹配项(两个表都包含工厂,地址,城市和州字段),您最终可以找到匹配项。当然,当你开始减少你LEFT的字符数时,准确性会受到影响。我在excel中做到了这一点并且收益率很高。任何人都可以推荐更好的解决方案吗?

9 个答案:

答案 0 :(得分:8)

您可能希望考虑用户定义函数(UDF)

SELECT ShiptoPlant, CleanString([ShiptoPlant]) AS Clean
FROM Table


Function CleanString(strText)
Dim objRegEx As Object

Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.Global = True

objRegEx.Pattern = "[^a-z0-9]"
CleanString = objRegEx.Replace(strText, "")

End Function

答案 1 :(得分:4)

您可以使用Access

中的内置Replace功能
SELECT
    Replace(Replace(Replace(ShipToPlant, "#", ""), "-", ""), "/", "") AS ShipToPlant
FROM
    BTST

正如其他人所说,在Access中你可以在VBA中编写自己的函数并在查询中使用它们。

修改

这是一种通过在我们自己的函数中包装Replace函数来处理嵌套的Replace限制的方法。感觉但是它可以将它放在Access

中的模块中
Public Function SuperReplace(ByRef field As String, ByVal ReplaceString As String) As String
    ' Size this as big as you need... it is zero-based by default' 
    Dim ReplaceArray(3) As String

    'Fill each element with the character you need to replace'  
    ReplaceArray(0) = "#"
    ReplaceArray(1) = "-"
    ReplaceArray(2) = "/"
    ReplaceArray(3) = " "

    Dim i As Integer
    For i = LBound(ReplaceArray) To UBound(ReplaceArray)    
       field = Replace(field, ReplaceArray(i), ReplaceString)
    Next i

    SuperReplace = field    
End Function

然后使用此查询进行测试

SELECT 
    SuperReplace(ShipToPlant,"") AS ShipToPlant
FROM
    BTST

您可能希望将其展开,以便您可以传入一个字符串数组,而不是将它们硬编码到函数中。

编辑2:

在回答有关问题的评论中的其他信息时,这里建议您如何以不同的方式处理这种情况。此apprach的优点是,一旦您在工厂名称中进行了映射排列,您将不需要在未来几年中对未来数据执行字符串替换,只需将新工厂名称和排列添加到地图中。

从创建另一个表开始,我们称之为plant_map

CREATE TABLE plant_map (id AUTOINCREMENT PRIMARY KEY, name TEXT, master_id LONG)

plant_map,添加工厂名称的所有排列,并在master_id字段中插入您希望用于引用特定工厂名称排列组的名称的id。根据您的评论,我将使用 Signode服务

INSERT INTO plant_map(name, master_id) VALUES ("Signode Service", 1);
INSERT INTO plant_map(name, master_id) VALUES ("Signode Svc", 1);
INSERT INTO plant_map(name, master_id) VALUES ("Signode - Service", 1);
INSERT INTO plant_map(name, master_id) VALUES ("Signode svc", 1);
INSERT INTO plant_map(name, master_id) VALUES ("SignodeService", 1);

现在,当您查询BTST表时,您可以使用

获取 Signode服务的数据
SELECT
    field1,
    field2
FROM
    BTST source
INNER JOIN
    (
    plant_map map1      
    INNER JOIN
    plant_map map2
    ON map1.master_id = map2.id
    )
    ON source.ShipToPlant = map1.name
WHERE
    map2.name = "Signode Service"

BTST中的数据可以保持不变。

基本上,这是将BTST中的工厂名称加入plant_map中的名称,然后使用master_idid内的plant_map自加入所以你只需传递一个“常用”名称。我建议在name中的每个列master_idplant_map上添加索引,因为这两个字段都将用于连接。

答案 2 :(得分:2)

不要认为Access支持CASE语句。考虑使用iif:

iif ( condition, value_if_true, value_if_false )

对于这种情况,您可以使用REPLACE功能:

SELECT 
    REPLACE(REPLACE(REPLACE(yourfield, '#', ''), '-', ''), '/', '') 
    as FieldName
FROM
    ....

答案 3 :(得分:1)

好的,你的问题已经改变了,所以解决方案也是如此。这有两种方法。快速而肮脏的方式只能部分解决您的问题,因为它无法解决更多奇怪的排列,如缺失空格或拼写错误的单词。快速而肮脏的方式:

  1. 创建一个新表 - 让我们调用它 TCHAR。
  2. 在其中放置一个文本字段 - 你要替换的字符 - 我们会 在此示例中将其称为char
  3. 将要删除的所有char或char combinatios放入此表中。
  4. 创建并运行以下查询。 请注意,它只会删除一个 项目一次,但你也可以 不同版本的相同 替换它也像' - '或 ' - ' 在本例中,我创建了一个名为tPlant的表,其中包含一个名为ShipToPlant的字段。

    SELECT tPlant.ShipToPlant, Replace([ShipToPlant], (SELECT top 1 char FROM tChar WHERE instr(ShipToPlant,char)<>0 ORDER BY len(char) Desc),"" ) AS New FROM tPlant;

  5. 更好(但更复杂)的方式。这种解释将是一般性的,因为几乎不可能将整个事情放在这里。如果您想直接与我联系,请在gmail上使用我的用户名:

    1. 创建限定符表 - 人们像svc一样进入的错误 而不是服务。你愿意 进入每个奇怪的排列你 得到。
    2. 使用QualifierID创建一个表格 工厂ID。在这里你会说哪个 限定词去哪个工厂。
    3. 创建一个连接两者的查询 和你的桌子有错误的植物 名字在里面。使用instr所以说什么 在田里。
    4. 创建第二个查询 加剧了第一个问题。伯爵 instr字段并将其用作分数。 得分最高的参赛作品是 工厂。
    5. 您必须手动输入 它找不到,但很快就会发现 你将拥有的将是无与伦比的 表中的条目越来越多。
    6. ughh


      您有两种不同的选择。在Access中,sql中没有CASE,您需要使用IIF。它不像更强大的数据库引擎中的解决方案那样优雅,并且需要为此实例嵌套,但它将为您完成工作。

      SELECT
          iif(instr(ShipToPlant,"#")<>0,"",
          iif(instr(ShipToPlant,"-")<>0,"",
          iif(instr(ShipToPlant,"/")<>0,"",ShipToPlant ))) AS FieldName
      FROM BTST;
      

      您也可以使用sql来限制数据。

      SELECT YourID, nz(aBTST.ShipToPlant,"") AS ShipToPlant  
      FROM BTST LEFT JOIN (
          SELECT YourID, ShipToPlant 
          FROM BTST 
          WHERE ShipToPlant NOT IN("#", "-", "/")
          ) as aBTST ON BTST.YourID=aBTST.YourID
      

      如果您了解VB,您也可以创建自己的函数并将它们放入查询...但这是另一篇文章。 :) HTH

答案 4 :(得分:1)

在代码模块中创建公共函数。

Public Function StripChars(ByVal pStringtoStrip As Variant, ByVal pCharsToKeep As String) As String

Dim sChar As String
Dim sTemp As String
Dim iCtr As Integer

  sTemp = ""

  For iCtr = 1 To Len(pStringtoStrip)
    sChar = Mid(pStringtoStrip, iCtr, 1)
    If InStr(pCharsToKeep, sChar) > 0 Then
      sTemp = sTemp & sChar
    End If
  Next

  StripChars = sTemp

End Function

然后在你的查询中

SELECT
    StripChars(ShipToPlant, "abcdefghijklmnopqrstuvwxyz0123456789") AS ShipToPlantDisplay  
FROM 
    BTST

注意 - 对于大量记录来说这将是很慢的 - 如果这是永久性的,那么使用相同的函数创建更新查询。

编辑:进行更新:

UPDATE BTST
    SET ShipToPlant = StripChars(ShipToPlant, "abcdefghijklmnopqrstuvwxyz0123456789")

答案 5 :(得分:0)


SELECT 
IIF
(
    Instr(1,ShipToPlant , "#") > 0 
    OR Instr(1,ShipToPlant , "/") > 0 
    OR Instr(1,ShipToPlant , "-") > 0, ""
    , ShipToPlant 
)
FROM BTST

答案 6 :(得分:0)

全部 - 我在两个单独的查询中嵌套REPLACE()函数。由于我需要替换超过35个非字母数字字符,并且Access将查询的复杂性限制在大约20个嵌套函数的某个地方,我只是将它分成两个进程。有点笨重,但它奏效了。在这种情况下应该遵循KISS原则。谢谢你的帮助!

答案 7 :(得分:0)

I know this is a really old question, but I stumbled over it whilst looking for a solution to this problem, but ended up using a different approach.

The field that I wish to update is called 'Customers'. There are 20-odd accented characters in the 'CustName' field for which I wish to remove the diacritics - so (for example) ã > a.

For each of these characters I created a new table 'recodes' with 2 fields 'char' and 'recode'. 'char' contains the character I wish to remove, and 'recode' houses the replacement.

Then for the replace I did a full outer join inside the update statement

UPDATE Customers, Recodes SET Customers.CustName = Replace([CustName],[char],[recode]);

This has the same effect as nesting all of the replace statements, and is a lot easier to manage.

答案 8 :(得分:0)

此查询获取前3个字符并将其替换为空白

示例:BO-1234
输出:1234

BO: IIf(IsNumeric(Left([sMessageDetails],3)),[sMessageDetails],Replace([sMessageDetails],Left([sMessageDetails],3),""))