1

我有一个搜索查询,包括一个表和两列。

Select * 
from Gecoserv _a 
where _a.typeOfEcosystem IN (:typeNameList) 
    AND _a.service IN (:serviceNameList)

它有效,但没有给出我想要的结果

比方说:

typeNameList = { Freshwater, Saltwater, Dunes }
serviceNameList = {Habitat, Food, Recreation }

我希望网格只显示单一组合(淡水栖息地、咸水食物和沙丘娱乐等),但它也显示其他组合(例如:淡水娱乐)。

换句话说:查询结果显示

[0,0],[0,1],[0,2],
[1,0],[1,1],[1,2],
[2,0],[2,1],[2,2].

但我只想看:[0,0],[1,1],[2,2]

我知道我必须在查询末尾添加一些内容。我尝试了很多东西,但无法找到完美的解决方案。

有没有人可以帮助我?

问候,

梅利赫

4

2 回答 2

0

如果没有任何支持架构甚至数据库制造商,我的回复将不得不对您的设置做出一些假设。

理论

这听起来很像笛卡尔积。您的问题的症结在于您的 IN 子句彼此独立。因此,单个记录可以同时满足这两个条件,但不是您希望在结果集中看到的。例如,我们有如下记录:

id   name          typeOfEcosystem  service
--   ------------  ---------------  ----------
 1   myEcoService  Freshwater       Recreation

正如现在所写的那样,您的查询将首先采用 typeOfEcosystem (Freshwater) 并检查它是否在您的列表中,它是。 结果:真

然后,查询将获取服务(娱乐)并检查它是否在第二个列表中。再次,找到了值。 结果:真

所有条件都为真,因此该行包含在最终结果集中。

解决方案

在我看来,您想要一个多列 IN 子句。请注意,这与您的尝试不同,因为最终解决方案应该只涉及一个 IN 关键字。现在,查询的确切形式在很大程度上取决于您的数据库及其提供的内容。应该适用于任何符合 SQL 的数据库的解决方案是使用字符串操作。一个粗略的例子如下:

Select * 
  from Gecoserv _a 
 where _a.typeOfEcosystem || '-' || _a.service IN ('Freshwater-Habitat', 'Saltwater-Food', 'Dunes-Recreation')

在此示例中,“||” 假定为串联。这是非常便携的,但它很慢。这个问题可以更优雅地解决,但确切的解决方案将更多地取决于您的特定数据库和要求。例如,Oracle 中的多列 in 子句可能很简单:

Select * 
  from Gecoserv _a 
 where (_a.typeOfEcosystem, _a.service) IN (('Freshwater', 'Habitat'), ('Saltwater', 'Food'), ('Dunes', 'Recreation'));

您可能必须对这些解决方案中的任何一个进行调整,以使它们与您的 DBMS 配合得很好。可以在以下位置找到更多技术(以及简单的老神阅读):

  1. IN子句中的SQL多列

  2. 选择使用 SQLite 重复列组合的记录

于 2013-01-17T21:42:12.690 回答
0

我认为这是无法做到的,因为这是您获得的唯一信息。参数值之间的关系是它们在列表中的位置。没有选项可以在 SQL 中关联和/或保留这些位置。

您对创建 sql 语句有什么影响?

如果您可以遍历代码中的列表并动态创建 sql,您可以找到如下解决方案:

SELECT * FROM Gecoserv 
  WHERE typeOfEcosystem = 'Freshwater' AND service = 'Habitat'
UNION
SELECT * FROM Gecoserv 
  WHERE typeOfEcosystem = 'Freshwater' AND service = 'Food'
UNION
...

有关如何参数化的示例,请查看此答案

另一种选择:拆分字符串

您的主要问题是在其列表中保留参数的“等级”/“位置”。这可以通过用字符串替换列表并将它们分解来完成。

我使用了这个在 sql 中拆分字符串的示例

生成的查询(使用此 Fiddle进行)如下所示:

DECLARE 
  @ecoSystems varchar(100) = 'Freshwater,Saltwater,Dunes',
  @services varchar(100) = 'Habitat,Food,Recreation',
  @separator char(1) = ','

SELECT
  [g].[id],
  [g].[typeOfEcoSystem],
  [g].[service]
FROM [dbo].[Split](@separator, @ecoSystems) [e]
INNER JOIN [dbo].[Split](@separator, @services) [s] 
  ON [e].[position] = [s].[position]
INNER JOIN [Gecoserv] [g] 
  ON [e].[part] = [g].[typeOfEcoSystem]
    AND [s].[part] = [g].[service]
ORDER BY [id] ASC

这适用于您的场景吗?(答案:是的,几乎……)

最后没有功能

DECLARE 
  @ecoSystems varchar(100) = 'Freshwater,Saltwater,Dunes',
  @services varchar(100) = 'Habitat,Food,Recreation',
  @separator char(1) = ',';

WITH [EcoSystemsAndServices]([posE], [startE], [endE], [posS], [startS], [endS])
AS 
(
  SELECT 
    1, 
    1, 
    CHARINDEX(@separator, @ecoSystems),
    1, 
    1, 
    CHARINDEX(@separator, @services)
  UNION ALL
  SELECT 
    [posE] + 1, 
    [endE] + 1, 
    CHARINDEX(@separator, @ecoSystems, [endE] + 1),
    [posS] + 1, 
    [endS] + 1, 
    CHARINDEX(@separator, @services, [endS] + 1)
  FROM [EcoSystemsAndServices]
  WHERE [endE] > 0
)


SELECT 
  [g].[id],
  [g].[typeOfEcoSystem],
  [g].[service]
FROM [Gecoserv] [g]
INNER JOIN [EcoSystemsAndServices] [ess] 
  ON [g].[typeOfEcoSystem] = SUBSTRING(@ecoSystems, 
                                        [startE], 
                                        CASE WHEN [endE] > 0 THEN [endE] - [startE] ELSE 100 END)
    AND [g].[service] = SUBSTRING(@services, 
                                  [startS], 
                                  CASE WHEN [endS] > 0 THEN [endS] - [startS] ELSE 100 END)
ORDER BY [id] ASC
于 2013-01-17T21:38:16.803 回答