【问题标题】:Table-less UNION query in MS Access (Jet/ACE)MS Access (Jet/ACE) 中的无表 UNION 查询
【发布时间】:2011-12-17 12:35:50
【问题描述】:

这按预期工作:

SELECT "Mike" AS FName

这会失败并出现错误“查询输入必须包含至少一个表或查询”:

SELECT "Mike" AS FName
UNION ALL
SELECT "John" AS FName

这只是 Jet/ACE 数据库引擎的一个怪癖/限制,还是我遗漏了什么?

【问题讨论】:

  • 这也是我发现的,但是您可以从任何一个行表中选择作为解决方法,或者从任何表中选择不同的。
  • 是的,我保留了一个包含 1 个字段和 1 个记录的本地虚拟表,并按照您的建议使用它。这只是让我有点恼火的事情,我想确保我没有错过一些简单的事情。
  • 我个人从未在 MSDN 上看到过任何与此相关的文档,因为您已经提到要使用现有的表名来绕过它。
  • MS Access reference for SELECT 没有将FROM 显示为可选。所以我想这是在其他地方解释的,在那里你没有完整的 SQL。这似乎与this question on SO 一致,如果您将子查询混合在一起,Access 需要FROM。 .. 当然,我还没有测试过所有这些。

标签: sql ms-access jet


【解决方案1】:

这是一个实际在访问中起作用的查询。我测试了它。它使用子查询作为“双重”:(SELECT count(*) as cnt from MSysObjects)。

select 1 as a from (SELECT count(*) as cnt from MSysObjects) UNION ALL
select 2 as a from (SELECT count(*) as cnt from MSysObjects) UNION ALL
select 3 as a from (SELECT count(*) as cnt from MSysObjects)

【讨论】:

  • 请在您的答案中添加一些解释,以便其他人可以从中学习。
【解决方案2】:

输入任何表名(您实际上不需要从中选择一列)。

此查询为我提供了下拉列表所需的 3 个会计年度。财政年度从 7 月开始。

SELECT IIf(Month(Now())>6,Year(Now())-1,Year(Now())-2) AS FY
FROM table
UNION 
SELECT IIf(Month(Now())>6,Year(Now()),Year(Now())-1) AS FY
FROM table
UNION 
SELECT IIf(Month(Now())>6,Year(Now())+1,Year(Now())) AS FY
FROM table;

【讨论】:

    【解决方案3】:

    这里有一个更简单的方法:

    SELECT 'foo', 'boo', 'hoo' from TableWith1Row
    union
    SELECT 'foo1', 'boo1', 'hoo1' from TableWith1Row
    

    重要提示:TableWith1Row 可以是具有字面上 1 条记录的表(无论如何您都可以忽略),也可以是具有任意行数的表(必须至少有 1 行),但您添加 WHERE 子句以确保 1 行.这有点松散,但它是一种快速完成这项工作而无需创建更多表的方法。

    【讨论】:

      【解决方案4】:

      当您限制了对数据库的只读访问权限(即您无法创建新表或访问系统资源)时,这可能会起作用:

      SELECT "Mike" AS FName
      FROM (SELECT COUNT(*) FROM anyTable WHERE 1=0) AS dual
      
      1. anyTable 是您找到的第一个用户表(我很难想象没有用户表的真实数据库!)。

      2. WHERE 1=0 应该快速返回 0,即使是在一张大桌子上也是如此(希望 Jet 引擎足够聪明,能够识别这种微不足道的情况)。

      【讨论】:

      • 我使用了你的方法并创建了一个名为 SingleRowQuery 的查询:SELECT Count(*) FROM UserTable WHERE 1=0; 然后我在联合查询中使用它:SELECT CustNo, CustName FROM Customers WHERE CustNo = 1 UNION SELECT 0, '(NA)' FROM SingleRowQueryORDER BY CustName
      • 我最喜欢这种方法,因为它避免了使用MSysObjects(通常存在阻止其使用的权限问题)。我自己的与 Oracle 的 DUAL 匹配的版本是:( SELECT TOP 1 IIF( COUNT(*) = 0, 'X', 'X' ) AS DUMMY FROM [anyTable] WHERE 1 = 0 ) AS [dual]
      【解决方案5】:

      如果有人想使用 Top 1 方法,它看起来像这样:

      SELECT first_name AS FName
      FROM tblname
      UNION ALL
      SELECT "Mike" as Fname
      FROM (Select Top 1 Count(*) FROM tblsometable);
      

      联合两侧的字段别名必须相同,在本例中为“FName”。

      【讨论】:

      • 为什么是Select Top 1 Count(*)?如果没有 GROUP BY 子句,Select Count(*) 只能返回一行。在那里包含TOP 1 有什么好处?
      【解决方案6】:

      如果您可以访问某些系统表,则可以通过这种方式模拟双表:

      (SELECT COUNT(*) FROM MSysResources) AS DUAL
      

      很遗憾,我不知道有任何系统表...

      • 始终可用、可读(并非每个连接都可以访问 MSysObject)
      • 只包含一条记录,例如 Oracle 的 DUAL 或 DB2 的 SYSIBM.DUAL

      所以你会写:

      SELECT 'Mike' AS FName
      FROM (SELECT COUNT(*) FROM MSysResources) AS DUAL
      UNION ALL
      SELECT 'John' AS FName
      FROM (SELECT COUNT(*) FROM MSysResources) AS DUAL
      

      例如,这就是在jOOQ 中作为句法元素实现的内容。

      【讨论】:

      • 我喜欢这个主意。如果您知道任何可用系统表的名称,您可以执行类似FROM (SELECT TOP 1 NULL FROM MSysResources) AS DUAL 的操作。TOP 1 限制为 1 行,NULL 是返回的单个值 - 它无需知道任何列名.您只需要一个表名。
      • 这可能大部分都有效,但根据经验,在某些特定的子查询情况下使用 TOP 时,所有 RDBMS 中总会有一个非常微妙的警告......
      • MSysResources 不存在于我正在使用的任何 MDB 数据库中。我确实有MSysObjects,但是在通过 OLE-DB 的查询中使用它们时出现权限错误。抱怨。
      • @Dai:你可以使用任何其他表
      【解决方案7】:

      你没有忽略任何事情。 Access 的数据库引擎将允许单行 SELECT 没有 FROM 数据源。但是,如果您想要 UNIONUNION ALL 多行,则必须包含 FROM ...,即使您没有引用该数据源中的任何字段。

      我创建了一个包含一行的表并添加了一个检查约束以确保它始终只有一行。

      Public Sub CreateDualTable()
          Dim strSql As String
          strSql = "CREATE TABLE Dual (id COUNTER CONSTRAINT pkey PRIMARY KEY);"
          Debug.Print strSql
          CurrentProject.Connection.Execute strSql
          strSql = "INSERT INTO Dual (id) VALUES (1);"
          Debug.Print strSql
          CurrentProject.Connection.Execute strSql
      
          strSql = "ALTER TABLE Dual" & vbNewLine & _
              vbTab & "ADD CONSTRAINT there_can_be_only_one" & vbNewLine & _
              vbTab & "CHECK (" & vbNewLine & _
              vbTab & vbTab & "(SELECT Count(*) FROM Dual) = 1" & vbNewLine & _
              vbTab & vbTab & ");"
          Debug.Print strSql
          CurrentProject.Connection.Execute strSql
      End Sub
      

      Dual 表对于这样的查询很有用:

      SELECT "foo" AS my_text
      FROM Dual
      UNION ALL
      SELECT "bar"
      FROM Dual;
      

      我见过的另一种方法是使用带有TOP 1SELECT 语句或将结果集限制为单行的WHERE 子句。

      注意检查约束是随 Jet 4 添加的,仅适用于从 ADO 执行的语句。 CurrentProject.Connection.Execute strSql 有效,因为 CurrentProject.Connection 是一个 ADO 对象。如果您尝试使用 DAO 执行相同的语句(即CurrentDb.Execute 或来自 Access 查询设计器),您将收到语法错误,因为 DAO 无法创建检查约束。

      【讨论】:

      • 我很想知道设计的原因或查看有关该行为的一些文档,但有足够多有经验的人参与进来,我相信这就是答案。
      • 谢谢。 @onedaywhen 向我展示了如何使用 ADO 进行检查约束;你不能用 DAO 做到这一点。我很欣赏 Dual 允许我编写简洁的单行查询......即使我实际上并不经常这样做。
      • @GordThompson 如果我的观点有误,请纠正我:您的设计允许不超过一行,但也允许删除这一行。我想保证Dual 始终只包含一行。
      • 不,你是对的,没有什么可以阻止我表中的那一行被删除。下次出现这种情况时,我会记住你的方法!
      • “检查约束...仅适用于从 ADO 执行的语句”——错误陈述。尽管 Access 查询设计器默认为 ANSI-89 查询模式,这确实会导致 CHECK 语法失败,但您可以将 Access 查询设计器更改为 ANSI-92 查询模式,从而使语法成功。 DAO 始终使用 ANSI-89 查询模式,而 ADO 始终使用 ANSI-92 查询模式。但设计师可以使用任何一种。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多