将列从 Varchar2 转换为 CLOB 后查询无法执行
Posted
技术标签:
【中文标题】将列从 Varchar2 转换为 CLOB 后查询无法执行【英文标题】:Query fails to execute after converting a column from Varchar2 to CLOB 【发布时间】:2016-05-20 08:36:15 【问题描述】:我有一个 oracle 查询
select id from (
select ID, ROW_NUMBER() over (partition by LATEST_RECEIPT order by ID) rownumber
from Table
where LATEST_RECEIPT in
(
select LATEST_RECEIPT from Table
group by LATEST_RECEIPT
having COUNT(1) > 1
)
) t
where rownumber <> 1;
LATEST_RECEIPT 的数据类型较早时为 varchar2(4000),此查询运行良好。由于需要扩展列的长度,我将其修改为 CLOB,之后失败。谁能帮我解决这个问题或提供解决方法?
【问题讨论】:
抛出什么错误? ORA00932- 不一致的数据类型- 预期:得到 clob 这是使用 CLOB/BLOB 的缺点之一,它们不能用于索引,也不能用于GROUP BY
和其他聚合函数。我认为您必须将其转换回 VARCHAR(much-longer)
data 类型
我有长度> 4000的数据。但是oracle中varchar2的最大大小是4000。所以,我们切换到CLOB
那么这是您的 DBMS 和您拥有的用例的真正问题。你真的需要按整个LAST_RECEIPT
文本分组吗? (如果是,那么您可能需要添加带有文本哈希键的另一列,然后将其用于按条件分组)
【参考方案1】:
您可以更改内部查询以查找具有相同 last_receipt
值但 ID 不同的其他行(假设 ID 是唯一的);如果存在另一行,则等于您的计数返回大于一。但是你不能简单地测试两个CLOB值是否相等,你需要使用dbms_lob.compare
:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
);
应用行号过滤器比较棘手,因为您也不能在分析 partition by
子句中使用 CLOB。正如 André Schild 所建议的,您可以使用哈希;这里传递整数值 3,相当于dbms_crypto.hash_sh1
(尽管理论上可能会在未来的版本中改变!):
select id from (
select ID, ROW_NUMBER() over (partition by dbms_crypto.hash(LATEST_RECEIPT, 3)
order by ID) rownumber
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
)
)
where rownumber > 1;
当然有可能发生哈希冲突,如果发生这种情况 - 您有两个 latest_receipt
值,它们都出现了不止一次并且都哈希到相同的值 - 那么您可能会返回太多行。这似乎不太可能,但值得考虑。
因此,您只能查找具有相同 lastest_receipt
和较低 ID 的行,而不是排序:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID < t1.ID
);
再次假设 ID 是唯一的。如果不是,那么您仍然可以改用rowid
,但您对找到哪些行的控制较少 - 最低的rowid
不一定是最低的ID。大概您正在使用它来删除要删除的行。如果您实际上不介意保留哪一行以及删除哪一行,那么您仍然可以这样做:
and t2.ROWID < t1.ROWID
但是由于您当前正在订购,这可能是不可接受的,尽管风险很小,但散列可能更可取。
【讨论】:
以上是关于将列从 Varchar2 转换为 CLOB 后查询无法执行的主要内容,如果未能解决你的问题,请参考以下文章
oracle修改字段类型由varchar2修改为clob类型
将列从 varchar 转换为 nvarchar 是不是会更改存储在列中的字符串的编码?