Бестабличный запрос UNION в MS Access (Jet / ACE) - PullRequest
26 голосов
/ 28 октября 2011

Это работает, как и ожидалось:

SELECT "Mike" AS FName

Это не работает с ошибкой «Ввод запроса должен содержать хотя бы одну таблицу или запрос»:

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

Это просто причуды /ограничение ядра базы данных Jet / ACE или я что-то упустил?

Ответы [ 6 ]

23 голосов
/ 28 октября 2011

Вы ничего не пропустили. Ядро доступа к базе данных разрешит одну строку SELECT без источника данных FROM. Но если вы хотите UNION или UNION 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;

Другой подход, который я видел, заключается в использовании оператора SELECT с предложением TOP 1 или WHERE, который ограничивает набор результатов одной строкой.

Примечание. Проверочные ограничения были добавлены в Jet 4 и доступны только для операторов, выполняемых из ADO. CurrentProject.Connection.Execute strSql работает, потому что CurrentProject.Connection является объектом ADO. Если вы попытаетесь выполнить тот же оператор с DAO (например, CurrentDb.Execute или из конструктора запросов Access), вы получите синтаксическую ошибку, потому что DAO не может создать проверочные ограничения.

6 голосов
/ 29 октября 2013

Если у вас есть доступ к некоторым системным таблицам, вы можете эмулировать двойную таблицу следующим образом:

(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 .

2 голосов
/ 14 декабря 2015

Если у вас ограничен доступ только для чтения к базе данных (т.е. вы не можете создавать новые таблицы или обращаться к системным ресурсам), это может работать:

SELECT "Mike" AS FName
FROM (SELECT COUNT(*) FROM anyTable WHERE 1=0) AS dual
  1. anyTable - первая найденная вами таблица пользователей (я с трудом могу представить реальную базу данных без таблицы пользователей!).

  2. WHERE 1 = 0 isдолжен быстро возвращать счет 0 даже на большом столе (надеюсь, двигатель Jet достаточно умен, чтобы распознавать такие тривиальные условия).

1 голос
/ 13 апреля 2016

Вот гораздо более простой способ сделать это:

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

Важно: TableWith1Row может ЛИБО быть таблицей с буквально 1 записью (которую вы все равно игнорируете) ИЛИ это может быть таблица с любым количеством строк(должен иметь по крайней мере 1 строку), но вы добавляете предложение WHERE для обеспечения 1 строки.Это немного непросто, но это быстрый способ сделать это без создания дополнительных таблиц.

0 голосов
/ 30 ноября 2018

Введите любое имя таблицы (вам не нужно фактически выбирать столбец из нее).

Этот запрос дает мне 3 финансовых года, которые мне нужны для выпадающего списка.Финансовый год начинается в июле.

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;
0 голосов
/ 11 июня 2015

Если кто-то хочет использовать метод Top 1, он будет выглядеть так:

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

Псевдоним поля должен быть одинаковым на обеих сторонах объединения, в данном случае «FName».

...