WITH 子句可以在不使用 Select 语句的情况下具有硬编码值吗?

Posted

技术标签:

【中文标题】WITH 子句可以在不使用 Select 语句的情况下具有硬编码值吗?【英文标题】:Can WITH clause have hardcoded values with out using Select statement? 【发布时间】:2021-05-14 15:18:27 【问题描述】:

我有一种情况,我需要加入两个不同的数据库(都是 oracle,我没有数据库链接)才能得到我的结果。我想知道是否可以使用 WITH 子句创建一个带有所有硬编码值的子查询,以加入其他数据库中的另一个表。我尝试了类似下面的方法

WITH TEMP AS (SELECT '8108428','8110729' FROM DUAL) 

但是它将值作为列提供,但我需要将它们作为行放在一列下,因此我可以加入其他表。有人可以帮我解决吗?我以前使用过 WITH 子句,但其中有一个查询,但这只能采用硬编码值

感谢您的回复!

【问题讨论】:

快速修复:UNION ALL. 我有 1000 个值,所以 UNION ALL 不是可行的选择,因为我必须重复 Select in WITH 子句的次数与我的值一样多 除了db链接,可以导出为csv,在oracle中创建外部表。 你如何“拥有”数千个值?他们已经在桌子上了吗?它们是来自另一个来源吗?明白你问错了问题;您问“我如何使用 WITH 子句中的两个值来执行 ”,但事实证明问题是您有数千个值,而不是如何使用 WITH 子句。也许 WITH 子句甚至不是最好的工具。 例如:您可以在一个数据库中创建一个 JSON 数组作为 CLOB,将该 JSON(通过任何方式)复制到另一个数据库,然后使用来自 JSON 的数据,就好像它是一个表,带有JSON_TABLE 函数。三个非常简单的步骤。 【参考方案1】:

你可以创建一个集合类型:

CREATE TYPE varchar2_10_table AS TABLE OF VARCHAR2(10);

然后,如果您期望做某事,例如:

WITH TEMP ( value ) AS (
  SELECT '8108428' FROM DUAL UNION ALL
  SELECT '8110729' FROM DUAL
)
SELECT *
FROM   your_table
WHERE  id IN ( SELECT value FROM temp )

然后您可以将WITH 子句替换为集合:

SELECT *
FROM   your_table
WHERE  id IN ( SELECT value FROM TABLE( varchar2_10_table( '8108428','8110729' ) ) )

或使用MEMBER OF 运算符:

SELECT *
FROM   your_table
WHERE  id MEMBER OF varchar2_10_table( '8108428','8110729' )

或者,如果您想保留 WITH 子句并将集合的值解压缩到其中:

WITH TEMP ( value ) AS (
  SELECT COLUMN_VALUE FROM TABLE( varchar2_10_table( '8108428','8110729' ) )
)
SELECT *
FROM   your_table
WHERE  id IN ( SELECT value FROM temp )

或者,将集合保存在WITH 子句中:

WITH TEMP ( collection_value ) AS (
  SELECT varchar2_10_table( '8108428','8110729' ) FROM DUAL
)
SELECT y.*
FROM   your_table y
       INNER JOIN temp t
       ON ( y.id MEMBER OF t.collection_value )

【讨论】:

【参考方案2】:

请试试这个。

WITH TEMP AS (
              SELECT '8108428'FROM DUAL
              union all
              SELECT '8110729' FROM DUAL 
             ) 

【讨论】:

我有 1000 个值,所以 UNION ALL 不是可行的选择,因为我必须重复 Select in WITH 子句的次数与我的值一样多【参考方案3】:

最简单的方法是将数据获取为 XMLTYPE 或 JSON,例如:

在第一个数据库中将数据作为 XMLTYPE 获取:

select xmltype(cursor(select table_name,num_rows from user_tables)) xmldata from dual;

XMLDATA
----------------------------------------
<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <TABLE_NAME>MY_DUAL</TABLE_NAME>
  <NUM_ROWS>1</NUM_ROWS>
 </ROW>
 <ROW>
  <TABLE_NAME>T</TABLE_NAME>
  <NUM_ROWS>10000</NUM_ROWS>
 </ROW>
 <ROW>
  <TABLE_NAME>T1</TABLE_NAME>
  <NUM_ROWS>1000</NUM_ROWS>
 </ROW>
 <ROW>
  <TABLE_NAME>T_DEL</TABLE_NAME>
  <NUM_ROWS>2678541</NUM_ROWS>
 </ROW>
</ROWSET>

并在另一个数据库上使用它:

SQL> select *
  2  from xmltable('/ROWSET/ROW'
  3  passing xmltype(q'[<?xml version="1.0"?>
  4  <ROWSET>
  5   <ROW>
  6    <TABLE_NAME>MY_DUAL</TABLE_NAME>
  7    <NUM_ROWS>1</NUM_ROWS>
  8   </ROW>
  9   <ROW>
 10    <TABLE_NAME>T</TABLE_NAME>
 11    <NUM_ROWS>10000</NUM_ROWS>
 12   </ROW>
 13   <ROW>
 14    <TABLE_NAME>T1</TABLE_NAME>
 15    <NUM_ROWS>1000</NUM_ROWS>
 16   </ROW>
 17   <ROW>
 18    <TABLE_NAME>T_DEL</TABLE_NAME>
 19    <NUM_ROWS>2678541</NUM_ROWS>
 20   </ROW>
 21  </ROWSET>
 22  ]')
 23  columns
 24    TABLE_NAME,
 25    NUM_ROWS number
 26  );

TABLE_NAME                       NUM_ROWS
------------------------------ ----------
MY_DUAL                                 1
T                                   10000
T1                                   1000
T_DEL                             2678541

【讨论】:

JSON 方式还是简单得多。 首先,oracle中的json还是很不稳定,有bug。其次,对于 json,没有像 xmltype(cursor)) 这样方便的构造函数。 欢迎您随时发布您自己的答案,不要用您的 cmets 对我的答案进行 ping 操作。 我会评论任何我认为合适的答案——有时受访者不喜欢它,没关系。在这种情况下,由于您已经编写了答案,我想也许您想自己添加 JSON 方法。既然你显然不喜欢它,我只是按照你的建议自己发布了。至于“Oracle 中的 JSON 仍然非常不稳定和错误”——我不同意;对于 12.1 来说确实如此,但至少从 12.2 开始就很好,尤其是对于这个用例的微不足道的应用。【参考方案4】:

三步流程:

    创建一个 JSON 字符串(一个对您的值进行编码的 JSON 数组)。 通过任何方式(可能在数据库之外,因为你说你 不能让他们互相交谈),从 源数据库到目标数据库。 在目标数据库中,在字符串上调用json_table。你可以用那个 直接在任何需要“源”表的查询中 - 在我的 下面的例子,我在with子句的视图中提取数据,所以 您可以使用它,就好像目标数据库中存在“源”表一样。

我没有在下面显示第 2 步,因为这并不是真正在数据库方面。您可以从第一个查询的输出中复制字符串,并将其通过电子邮件发送给自己,或者将其保存在文件中并通过电子邮件发送文件,然后从目标端的文件中导入。

对于第 1 步,假设我需要从 SCOTT.EMP 复制“名称”。就这么简单

select json_arrayagg(ename) as json_str from scott.emp;

结果看起来像这样(注意我为了可读性添加了一个换行符,它实际上不在输出中):

JSON_STR
------------------------------------------------------------------------------
["SMITH","ALLEN","WARD","JONES","MARTIN","BLAKE","CLARK","SCOTT",
 "KING","TURNER","ADAMS","JAMES","FORD","MILLER"]

对于第 3 步,假设我复制了那个长字符串并将其粘贴到下面的查询中。 (您可能需要一些不同的东西来将字符串从一个位置移动到另一个位置 - 由您自己考虑。)with 子句中创建的视图替换了您在原始问题中尝试创建的视图。

with
  local_emp_copy (emp) as (
    select ename
    from   json_table(
             '["SMITH","ALLEN","WARD","JONES","MARTIN","BLAKE","CLARK","SCOTT",
               "KING","TURNER","ADAMS","JAMES","FORD","MILLER"]'
             , '$[*]' columns ename varchar2 path '$')
  )
select emp from local_emp_copy  --  or do whatever you need with the view
;
  
EMP      
---------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

在您最初的问题中,您正在创建一个包含一列字符串的视图,即使这些字符串实际上是数字。如果值是数字而不是字符串,则不应将它们括在引号中。在 JSON 方法中,如果您有一列数字,则第一步是相同的。第三步,在json_tablecolumns子句中,将列的数据类型改为number。就这么简单。

【讨论】:

【参考方案5】:

您可以使用IN 运算符,将元组传递给它,其中允许超过 1000 个值。这样您就不必担心如何将长字符串文字传递给数据库。

select *
from dual
where (1, dummy) in (
  (1, 'X'),
  (1, 'X')
)

db<>fiddle is here

如何生成这个元组列表:

您可以像现在一样从 Excel 中的第一个数据库中导出带有 ID 的结果,并通过连接生成列表。 您可以使用来自 SQL*Plus/SQL Developer 的相同 select 语句使用 spool 生成它:
set heading off
set feedback off
set pages 0
set linesize 1000
set colsep ' '
set serveroutput on
spool c:\temp\myresult.txt

select
  '(1,' ,
  id_col,
  ')' as tuple
from my_source_table;

spool off

【讨论】:

【参考方案6】:

每个 SELECT 返回一个表,表中的每一列都必须有一个名称;因此,您需要的是公用表表达式中的列名:

WITH cteTEMP AS (SELECT '8108428' AS SOME_VAL FROM DUAL UNION ALL
                 SELECT '8110729' AS SOME_VAL FROM DUAL) 
SELECT *
  FROM cteTemp

稍后在评论中您提到您有数千个值。也许您可以使用正则表达式从字符串中解析出您的值:

WITH cteCsv AS (SELECT '8108428,8110729' AS CSV FROM DUAL),
     cteVals AS (SELECT REGEXP_SUBSTR(CSV, '[^,]+', 1, LEVEL) AS SOME_VAL
                   FROM CSV
                   CONNECT BY REGEXP_SUBSTR(CSV, '[^,]+', 1, LEVEL) IS NOT NULL)
SELECT *
  FROM cteVals

db<>fiddle here

【讨论】:

以上是关于WITH 子句可以在不使用 Select 语句的情况下具有硬编码值吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL:with 查询

如何使用带有 WITH 子句的 INSERT 语句?

GREENPLUM中的with,即CTE用法,转自gp中文网文档

SQL Server - 在 INSERT 语句中使用 WITH 子句

我可以在 IN 子句中使用 WITH 子句中的表吗?

SQL 中with的用法