我有一个名为Ads
的表和另一个名为AdDetails
的表,用于以属性/值样式存储每个广告的详细信息。以下是使用虚拟代码的简化示例:
[AdDetailID], [AdID], [PropertyName], [PropertyValue]
2 28 Color Red
3 28 Speed 100
4 27 Color Red
5 28 Fuel Petrol
6 27 Speed 70
如何选择与PropertyName和PropertyValue的多种组合匹配的Ads
,例如:
where PropertyName='Color' and PropertyValue='Red'
And
where PropertyName='Speed' and CAST(PropertyValue AS INT) > 60
答案 0 :(得分:2)
你可能会做很多这样的事情,所以我会先制作一个将所有属性折叠成一行的视图。
create view vDetail
as
select AdID,
max(case PropertyName
when 'Color' then PropertyValue end) as Color,
cast(max(case PropertyName
when 'Speed' then PropertyValue end) as Int) as Speed,
max(case PropertyName
when 'Fuel' then PropertyValue end) as Fuel
from AdDetails
group by AdID
这种方法也解决了将速度转换为int的问题。
然后,如果我select * from vDetails
这使得在加入父表时很容易处理。你说你需要一个可变数量的"匹配" - 请注意下面的where子句。 @MatchesNeeded将是非空变量数的计数。
select *
from Ads a
inner join vDetails v
on a.AdID = v.AdID
where case when v.Color = @Color then 1 else 0 end +
case when v.Spead > @Speed then 1 else 0 end +
case when v.Fuel = @Fuel then 1 else 0 end = @MatchesNeeded
答案 1 :(得分:1)
我认为你有两个主要问题需要解决。
1)您需要能够将varchar值CAST为整数,其中某些值不会是整数。
如果您使用的是SQL 2012,则可以使用TRY_CAST()(sql server - check to see if cast is possible)。由于您使用的是SQL 2008,因此需要CASE和ISNUMERIC()的组合。
2)您需要一种有效的方法来检查是否存在多个属性。
我经常看到联接和where子句的组合,但我认为这很快就会变得混乱,因为你检查的属性数量会超过......比如说。相反,使用EXISTS子句往往更整洁,我认为它为SQL Optimizer提供了更好的线索。
SELECT AdID
FROM Ads
WHERE 1 = 1
AND EXISTS (
SELECT 1
FROM AdDetails
WHERE AdID = Ads.AdID
AND ( PropertyName='Color' and PropertyValue='Red' )
)
AND EXISTS (
SELECT 1
FROM AdDetails
WHERE AdID = Ads.AdID
AND PropertyName='Speed'
AND
(
CASE
WHEN ISNUMERIC(PropertyValue) = 1
THEN CAST(PropertyValue AS INT)
ELSE 0
END
)
> 60
)
您可以根据需要添加任意数量的EXISTS子句,而不会使查询特别难以阅读。
答案 2 :(得分:0)
这样的事情可能适用于2个条件,你必须根据条件的数量进行调整
select a.*
from ads as a
join addetails as d1 on d1.adid = a.id
join addetails as d2 on d2.adid = a.id
where (d1.PropertyName='Color' and d1.PropertyValue='Red')
and (d2.PropertyName='Speed' and d2.CAST(PropertyValue AS INT) > 60)
答案 3 :(得分:0)
DECLARE @AdDetails TABLE
(
AdDetailID INT,
AdID INT,
PropertyName VARCHAR(20),
PropertyValue VARCHAR(20)
)
INSERT INTO @AdDetails
( AdDetailID, AdID, PropertyName, PropertyValue )
VALUES
(2, 28, 'Color', 'Red'),
(3, 28, 'Speed', '100'),
(4, 27, 'Color', 'Red'),
(5, 28, 'Fuel', 'Petrol'),
(6, 27, 'Speed', '70');
--Col1
DECLARE @ColorValue VARCHAR(20) = 'Red'
--Col2
DECLARE @SpeedValue INT = 90
DECLARE @SpeedType VARCHAR(2) = '>'
--Col3
DECLARE @FuelValue VARCHAR(20) = null
SELECT DISTINCT a.AdID FROM @AdDetails a
INNER JOIN
(
SELECT *
FROM @AdDetails
WHERE @ColorValue IS NULL
OR @ColorValue = PropertyValue
) Color
ON Color.AdID = a.AdID
INNER JOIN
(
SELECT *
FROM @AdDetails
WHERE @SpeedType IS NULL
UNION
SELECT *
FROM @AdDetails
WHERE PropertyName = 'Speed'
AND ((@SpeedType = '>' AND CONVERT(INT, PropertyValue) > @SpeedValue)
OR (@SpeedType = '<' AND CONVERT(INT, PropertyValue) < @SpeedValue)
OR (@SpeedType = '=' AND CONVERT(INT, PropertyValue) = @SpeedValue))
) AS Speed
ON Speed.AdID = a.AdID
INNER JOIN
(
SELECT *
FROM @AdDetails
WHERE @FuelValue IS NULL
OR (@FuelValue = PropertyValue)
) AS Fuel
ON Fuel.AdID = a.AdID
我为每个属性类型添加一个内部连接子句(带有一些覆盖),你的sql查询将一次性传递所有可能的属性类型信息,将他们不想要的东西归零。代码非常难看,但随着它的增长。