【问题标题】:WHERE IN Question哪里有问题
【发布时间】:2010-10-29 12:07:00
【问题描述】:

我已经以表格形式提供了几个表格

主记录

Record ID  Details  Textual Information  
    1      AAAAAAA  ... some text referring to Oxford St Giles...  
    2      BBBBBBB  ... some text referring Oxford....  
    3      CCCCCCC  ... some text referring to Oxford St Aldate...  

及配套桌

地名

Record ID   PlaceName
    1        Oxford  
    1        St  
    1        Giles  
    2        Oxford  
    3        Oxford  
    3        St  
    3        Aldate  

我希望能够构建一个搜索词,以便可以在地名上输入全部或部分词。例如。如果我输入 'Oxford' 我会得到所有 3 条记录,如果我输入 'Oxford' 和 'Giles' 我只会得到记录 1 - 几乎就像使用 WHERE IN ('Oxford','Giles') 但条款是 ANDed 而不是被ORed?

不知道能不能做到这一点?我试过各种子查询都没有成功

我使用的是 SQL Server 2008

我想避免创建全文搜索字段

任何清除迷雾的指针都会非常有帮助。

* 更新主记录详细信息以避免混淆 *
两个表之间的唯一联系是记录 ID

** 于 11 月 3 日更新了示例表

CREATE TABLE MAIN_RECORD (RecordID int,DocumentRef varchar(100));  
INSERT INTO MAIN_RECORD VALUES (86, 'Doc Referring to William Samuel ADAMS');  
INSERT INTO MAIN_RECORD VALUES (87, 'Doc Referring to William JONES');  
INSERT INTO MAIN_RECORD VALUES (88, 'Doc Referring to Samuel SMITH');  



CREATE TABLE FORENAMES (RecordID int,Forename varchar(25));  
INSERT INTO FORENAMES VALUES (86, 'William');  
INSERT INTO FORENAMES VALUES (86, 'Samuel');  
INSERT INTO FORENAMES VALUES (87, 'William');  
INSERT INTO FORENAMES VALUES (88, 'Samuel');  

我的初始查询是

SELECT main.[RecordID],main.documentRef  
    FROM [MAIN_RECORD] main  
    INNER JOIN  [FORENAMES] fn  
    ON main.RecordID = fn.RecordID  
    WHERE fn.ForeName IN ('William')  

这很好并且返回

RecordID    documentRef  
86  Doc Referring to William Samuel ADAMS  
87  Doc Referring to William JONES  

Samuel 等等

我的问题是当我在名字搜索字段中有超过 1 个条目时,即

SELECT main.[RecordID],main.documentRef  
    FROM [MAIN_RECORD] main  
    INNER JOIN  [FORENAMES] fn  
    ON main.RecordID = fn.RecordID  
    WHERE fn.ForeName IN ('William,Samuel') 

这不返回任何内容。

我只需要返回包含 Samuel 和 William 的主记录,即。当搜索词中包含多个名称时。
它还需要找到威廉塞缪尔以及塞缪尔威廉。

从其他人的帖子中,我走上了 DIVISION 的路线并提出了以下内容(在 main SELECT 之前放置了一些字符串操作):

DECLARE @Forename nvarchar(max)
DECLARE @SQLCommand nvarchar(max)
DECLARE @Number_of_Terms int
SET @Forename = 'William,Samuel'
--SET @Forename = 'Samuel,William'
--SET @Forename = 'William'
--SET @Forename = 'Samuel'
SET @Number_of_Terms = LEN(@Forename) - LEN(REPLACE(@Forename,',',''))+1
SET @Forename = REPLACE(@Forename,',',''',''') 

SET @SQLCommand = 'SELECT fr.RecordID FROM dbo.BRS109_FullRecord fr '+
'INNER JOIN dbo.BRS109_Forenames fn '+
'ON fr.RecordID = fn.RecordID '+
'WHERE fr.RecordID = fn.RecordID '+
'AND fn.forename IN ('''+@Forename +''') ' +
' GROUP BY fr.RecordID ' +
' HAVING COUNT(fr.RecordId) = ' + CAST(@Number_of_Terms AS varchar(2)) +
' ORDER BY fr.RecordId'

EXECUTE sp_executesql @SQLCommand

这似乎给了我想要的东西。

非常感谢大家的贡献,尤其是“Quassnoi”和“onedaywhen”——非常有帮助

【问题讨论】:

  • 只是出于好奇:为什么要通过各种方式避免全文搜索??
  • 当然不是绝对 - 我们将在最初的紧迫性结束后查看它。
  • 请注意,这个地方实际上叫做 St Aldate's (en.wikipedia.org/wiki/St_Aldate's,_Oxford) ;)
  • 学究警告! - 但不是根据 1736 年去世的艾萨克培根的遗嘱 :-)

标签: sql sql-server-2008 search subquery


【解决方案1】:
SELECT  *
FROM    mainrecord mr
WHERE   (
        SELECT  COUNT(*)
        FROM    placenames pn
        WHERE   pn.record = mr.record
                AND pn.placename IN ('Oxford', 'St', 'Giles')
        ) = 3

【讨论】:

  • 我对这种横向思维过程印象深刻——在第一次测试中,它似乎完全符合我的要求。谢谢:-)
【解决方案2】:

你是在暗指relational division吗?例如提供所有产品的供应商,可以驾驶所有飞机的飞行员,等等?

如果是这样,this article 在 SQL 中有很多示例实现。

这是使用您的数据的一个:

WITH MainRecord (Record_ID, Details, Textual_Information)
     AS
     (
      SELECT Record_ID, Details, Textual_Information
        FROM (
              VALUES (1, 'AAAAAAA', ' ... some text referring to Oxford St Giles... '), 
                     (2, 'BBBBBBB', ' ... some text referring Oxford.... '), 
                     (3, 'CCCCCCC', '  ... some text referring to Oxford St Aldate... ')
             ) AS MainRecord (Record_ID, Details, Textual_Information)
     ), 
     PlaceNames (Record_ID, PlaceName)
     AS
     (
      SELECT Record_ID, PlaceName
        FROM (
              VALUES (1, 'Oxford'), 
                     (1, 'St'), 
                     (1, 'Giles'), 
                     (2, 'Oxford'), 
                     (3, 'Oxford'), 
                     (3, 'St'), 
                     (3, 'Aldate')
             ) AS PlaceNames (Record_ID, PlaceName)
     ),
     FullSet (Record_ID, PlaceName, Textual_Information)
     AS
     (
      SELECT DISTINCT P1.Record_ID, P1.PlaceName, 
             M1.Textual_Information 
        FROM MainRecord AS M1
             CROSS JOIN PlaceNames AS P1
     ), 
     NoMatch (Record_ID, PlaceName, Textual_Information)
     AS 
     (
      SELECT F1.Record_ID, F1.PlaceName, F1.Textual_Information
        FROM FullSet AS F1
              EXCEPT
      SELECT P1.Record_ID, P1.PlaceName, 
             M1.Textual_Information 
        FROM MainRecord AS M1
             INNER JOIN PlaceNames AS P1
               ON M1.Textual_Information LIKE '%' + P1.PlaceName + '%'
      )
SELECT F1.Record_ID, F1.PlaceName, F1.Textual_Information
  FROM FullSet AS F1
 WHERE NOT EXISTS (
                    SELECT * 
                      FROM NoMatch AS N1
                     WHERE N1.Record_ID = F1.Record_ID
                           AND N1.Textual_Information = F1.Textual_Information
                   );

更新:

我更喜欢你的原始数据;)不管怎样,我建议的方法是相同的,即关系划分(这次有更好的连接):

WITH FullSet (RecordID, Forename, DocumentRef)
     AS
     (
      SELECT DISTINCT P1.RecordID, P1.Forename, 
             M1.DocumentRef 
        FROM MAIN_RECORD AS M1
             INNER JOIN FORENAMES AS P1
                ON M1.RecordID = P1.RecordID
     ), 
     NoMatch (RecordID, Forename, DocumentRef)
     AS 
     (
      SELECT F1.RecordID, F1.Forename, F1.DocumentRef
        FROM FullSet AS F1
              EXCEPT
      SELECT P1.RecordID, P1.Forename, 
             M1.DocumentRef 
        FROM MAIN_RECORD AS M1
             INNER JOIN FORENAMES AS P1
               ON M1.RecordID = P1.RecordID
                  AND M1.DocumentRef LIKE '%' + P1.Forename + '%'
      )
SELECT F1.RecordID, F1.Forename, F1.DocumentRef
  FROM FullSet AS F1
 WHERE NOT EXISTS (
                    SELECT * 
                      FROM NoMatch AS N1
                     WHERE N1.RecordID = F1.RecordID
                           AND N1.DocumentRef = F1.DocumentRef
                   );

【讨论】:

  • 哎哟!这是一些要构建的查询,特别是当我的 MainRecord 可能需要超过 50,000 个条目时。我将使用更多示例和示例表来编辑我的原始问题。
  • 我仍然认为你没有理解我的问题。在您的更新版本中,您有一个子句“AND M1.DocumentRef LIKE '%' + P1.Forename + '%' 我想从 MainRecord 表中包含的唯一字段是 RecordID。所以,我的选择确实需要说“给我是 MainRecords 的 RecordID,它链接了 ForeName 记录,其中包含名字“William”和名字“Samuel” 这有意义吗?这个逻辑还需要扩展到姓氏字段,但我可以简单地重现。
【解决方案3】:

不完全确定在您的环境中,但在 Oracle 中,这个应该可以工作。

select * from mainrecord
where placename like '%Oxford%'
INTERSECT
select * from mainrecord
where placename like '%Giles%'

【讨论】:

    【解决方案4】:

    使用 LIKE 语句 像这样:

    SELECT * 
    FROM table AS t 
    WHERE t.PlaceName LIKE "%Oxford%" AND t.PlaceName LIKE "%Giles%"
    

    使用此查询,您将不需要第二个表, 一切都将通过第一个完成

    【讨论】:

      【解决方案5】:

      如果这就是你所需要的,你可以使用 LIKE

      SELECT PlaceName FROM PlaceNames WHERE PlaceName LIKE "%Oxford%" AND PlaceName LIKE "%Giles%"
      

      如果您需要更多的灵活性(例如限制匹配整个单词),您可以轻松add Regex search to SQL Server

          SELECT PlaceName FROM PlaceNames WHERE dbo.RegExMatch(PlaceName,'\bOxford\b') = 1 
      AND dbo.RegExMatch(PlaceName,'\bGiles\b') = 1
      

      【讨论】:

        猜你喜欢
        • 2021-05-21
        • 1970-01-01
        • 2019-11-02
        • 1970-01-01
        • 1970-01-01
        • 2021-09-28
        • 1970-01-01
        • 2016-09-18
        相关资源
        最近更新 更多