Table-less UNION query in MS Access (Jet/ACE)

自闭症网瘾萝莉.ら 提交于 2019-11-26 02:14:49

问题


This works as expected:

SELECT \"Mike\" AS FName

This fails with the error \"Query input must contain at least one table or query\":

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

Is this just a quirk/limitation of the Jet/ACE database engine or am I missing something?


回答1:


You didn't overlook anything. Access' database engine will allow a single row SELECT without a FROM data source. But if you want to UNION or UNION ALL multiple rows, you must include a FROM ... even if you're not referencing any field from that data source.

I created a table with one row and added a check constraint to guarantee it will always have one and only one row.

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

That Dual table is useful for queries such as this:

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

Another approach I've seen is to use a SELECT statement with TOP 1 or a WHERE clause which restricts the result set to a single row.

Note check constraints were added with Jet 4 and are only available for statements executed from ADO. CurrentProject.Connection.Execute strSql works because CurrentProject.Connection is an ADO object. If you try to execute the same statement with DAO (ie CurrentDb.Execute or from the Access query designer), you will get a syntax error because DAO can't create check constraints.




回答2:


If you have access to some system tables, you can emulate a dual table this way:

(SELECT COUNT(*) FROM MSysResources) AS DUAL

Unfortunately, I'm not aware of any system tables that...

  • are always available, readable (MSysObjects might not be accessible to every connection)
  • contain exactly one record, such as Oracle's DUAL or DB2's SYSIBM.DUAL

So you'd write:

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

This is what is being implemented as a syntactic element in jOOQ, for instance.




回答3:


When you have restricted read-only access to the database (i.e you cannot create new tables or access system resources), this could work:

SELECT "Mike" AS FName
FROM (SELECT COUNT(*) FROM anyTable WHERE 1=0) AS dual
  1. anyTable is the first user table you find (I can hardly imagine a real-life database with no user table!).

  2. WHERE 1=0 is supposed to return fast a count of 0, even on a large table (hopefully the Jet engine is smart enough to recognize such trivial condition).




回答4:


Here's a much simpler way to do it:

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

Important: TableWith1Row can EITHER be a table with literally 1 record (which you ignore anyway) OR it can be a table with any number of rows (must have AT LEAST 1 row) but you add a WHERE clause to ensure 1 row. This is a bit loosy-goosy but it's a quick way to make this work without creating more tables.




回答5:


If someone wants to use the Top 1 method, it would look like this:

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

The alias for the field has to be the same on both sides of the union, in this case "FName".




回答6:


Put in any table name (you don't need to actually select a column from it).

This query gives me the 3 fiscal years I need for a dropdown. The fiscal year begins in July.

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;


来源:https://stackoverflow.com/questions/7933518/table-less-union-query-in-ms-access-jet-ace

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!