根据记录的标准查询

时间:2016-04-25 07:16:21

标签: sql sql-server

有两个表,

Categories(Id, Name)
Products(Id, Text, CategoryId)

还有另一个过滤产品的表格:

Filter(CategoryIds, ContainText)

Filter表中的CategoryIds是逗号分隔的:100,101

我们希望根据从过滤表格中提取的标准来查询产品。

实施例: 过滤器只有两个记录:

CategoryIds   |   ContainText
-----------------------------
100,101       |   A
200,201       |   B

以下是我们要查询的产品:

containting text 'B' in categories 200 or 201

或者

FB.init({
    appId   : '{app-id}',
    cookie  : true,
    version : 'v2.5'
});

我们不想使用动态查询。

感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

根据Giorgos的评论,您需要规范化Filter表。如果你坚持这个设计,这是一个解决方案:

首先,您需要一个字符串拆分器。这是一张取自Aaron Bertrand的article

CREATE FUNCTION dbo.SplitStrings_XML
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );

然后,您需要将Filter表拆分为单独的行。然后在JOINProducts和拆分的Categories表格上Filter进行操作:

WITH CteFilter(CategoryId, ContainText) AS(
    SELECT
        CAST(s.Item AS INT), f.ContainText
    FROM Filter f
    CROSS APPLY dbo.SplitStrings_XML(f.CategoryIds, ',') s
)
SELECT p.*
FROM Products p
INNER JOIN Categories c
    ON c.Id = p.CategoryId
INNER JOIN CteFilter f
    ON f.CategoryId = c.Id
    AND p.Text LIKE '%' + f.ContainText + '%'

答案 1 :(得分:1)

假设可以进行规范化,这里有一个简单的查询,可以解决这个问题:

创建样本表:

DECLARE @Categories as table 
(
    id int identity(1,1), 
    name char(1)
)

DECLARE @Products as table 
(
    id int identity(1,1), 
    name varchar(100),
    categoryId int
)

DECLARE @Filter as table 
(
    id int identity(1,1), 
    ContainText varchar(100)
)

DECLARE @FilterCategories as table 
(
    FilterId int, 
    CategoryId int,
    PRIMARY KEY(filterId, CategoryId)
)

填充样本表:

INSERT INTO @Categories 
VALUES ('A'),('B'),('C'),('D'),('E')

INSERT INTO @Products (name, categoryId) 
VALUES ('cat A', 1),('category A', 1), ('cat B', 2), ('category B', 2)

INSERT INTO @Filter 
VALUES ('gory'), ('cat')

INSERT INTO @FilterCategories 
VALUES (1, 1), (1, 2), (2, 1)

查询:

SELECT  DISTINCT p.id As ProductId, 
        p.name As ProductName,
        c.name As CategoryName
FROM @Filter f
INNER JOIN @FilterCategories fc ON(f.id = fc.FilterId)
INNER JOIN  @Products p ON(p.categoryId = fc.CategoryId)
INNER JOIN @Categories c ON(p.categoryId = c.id)
WHERE p.name LIKE '%'+ f.ContainText +'%'

<强>结果:

ProductId   ProductName                    CategoryName
----------- ------------------------------ ------------
1           cat A                          A
2           category A                     A
4           category B                     B

答案 2 :(得分:1)

您还可以尝试以下不需要规范化的查询。因此,如果您无法更新表格,请尝试以下简单JOIN

的查询
--create table categories(id int, name varchar(100));
--create table products(id int, text varchar(100), categoryid int);
--create table filters(categoryids varchar(100), containtext varchar(10));
--insert into filters values
--('100,101','A'),
--('200,201','B');
--insert into products values 
--(1,'Random',100),
--(2,'rps',101),
--(3,'rps',200),
--(4,'rpsb',201);
--insert into categories values 
--(100,'Cat100'),
--(101,'Cat101'),
--(200,'Cat200'),
--(201,'Cat201');

select 
  P.*,C.Name 
from Products P join Categories C 
      on C.Id=P.CategoryId 
join Filters F 
     on text like '%'+ContainText+'%' 
          and   ','+ CategoryIds +',' like '%,'+cast(C.ID as varchar(100)) +',%'

答案 3 :(得分:0)

要标准化Filters表,您可以使用以下内容:

DECLARE @xml xml

;WITH Filters AS (
SELECT *
FROM (VALUES
('100,101', 'A'),
('200,201', 'B'),
('300,301,302', 'C')
) as t(CategoryIds, ContainText)
)

SELECT @xml =(
SELECT CAST ('<b>' + ContainText + '<a>'+REPLACE(CategoryIds,',','</a><a>') + '</a></b>' as xml)
FROM Filters
FOR XML PATH('')
)

SELECT  t.v.value('../.','nvarchar(1)') as ContainText,
        t.v.value('.','int') as CategoryId
FROM @xml.nodes('/b/a') as t(v)

输出:

ContainText CategoryId
----------- -----------
A           100
A           101
B           200
B           201
C           300
C           301
C           302

(7 row(s) affected)

然后,您可以将此表加入ProductsCategories

SELECT *
FROM NormFilters nf
INNER JOIN Categories c  -- (Id, Name)
    ON nf.CategoryId = c.Id
INNER JOIN Products p  --(Id, Text, CategoryId)
    ON c.CategoryId = p.CategoryId AND p.[Text] LIKE '%' + nf.ContainText +'%'