【问题标题】:Is there a way to use a part of varchar2 string as a primary key in oracle sql?有没有办法使用 varchar2 字符串的一部分作为 oracle sql 中的主键?
【发布时间】:2015-09-10 09:02:41
【问题描述】:

我需要用两列连接两个表。

第一个表的主键为整数类型 id。

第二张表有 varchar2 类型的列,该列包含相同的主键,但位于字符串的中间。

例如,在第一个表中,我有一列称为整数 ID 和 ID 像 1234。在第二个表中,我有一列带有类似字符串的列 'abcdefgh - 1234 (ijklmno)'。

有没有办法使用那个嵌套键?

【问题讨论】:

  • 将 table1 的 id 作为 table2 列的一部分是一个坏主意。在 table2 中有一个 int 列,table1 的外键!
  • 包含主键作为字符串的列的模式是否始终相同?
  • @jarlh 我没有设计这些表格。它是很久以前创建的。
  • 如果第二个表格列的模式始终相同,那么可以这样做
  • @Rahul Tripathi 实际上有不同的模式,但我只需要使用其中的一种,所以最后 - 是的。

标签: sql oracle


【解决方案1】:

从 Oracle 11g 开始,您可以使用虚拟列:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE TableA (
  id NUMBER(4,0) PRIMARY KEY
);

INSERT INTO TableA VALUES ( 1234 );

CREATE TABLE TableB (
  data VARCHAR2(25) NOT NULL
                    CHECK ( REGEXP_LIKE( data, '\w+ - (0|[1-9]\d{0,3}) \(\w+\)' ) ),
  id   NUMBER(4,0)  GENERATED ALWAYS AS ( TO_NUMBER( REGEXP_SUBSTR( data, '\w+ - (0|[1-9]\d{0,3}) \(\w+\)', 1, 1, null, 1 ) ) ) VIRTUAL,
  CONSTRAINT TableB__ID__FK FOREIGN KEY ( id ) REFERENCES TableA ( id )
);

INSERT INTO TableB ( data ) VALUES ( 'abcdefgh - 1234 (ijklmno)' );

查询 1

SELECT * FROM TableB

Results

|                      DATA |   ID |
|---------------------------|------|
| abcdefgh - 1234 (ijklmno) | 1234 |

【讨论】:

  • 不幸的是,它是 Oracle 10gR2。
  • @Lukasz-K2 那么你不能使用 VIRTUAL 列。您可以更改表设计,这是一个永久的解决方案。或者,使用我在回答中提到的解决方法。或者,升级到 11g 并使用 MTO 的解决方案,这也是一个不错的选择。
  • 您还可以为ID 添加一个永久的非虚拟列,并使用更新和插入触发器确保一致性(这将尝试复制虚拟列的行为)。然后,您可以将外键应用于此列。触发器有它们的位置,但我倾向于避免使用它们,所以虽然它是一种解决方案,但我觉得它并不吸引人。
【解决方案2】:

首先,桌子设计不好。您应该在第二个表中有 主键 列与 外键关系。并为外键提供支持索引。将分隔文本存储在单列中并不是一个好的设计,并且违反了规范化

如果第二个表中所有行的字符串模式都相同,那么您可以使用 SUBSTR 提取所需的字符串,然后 join 与第一个表。

例如,

SQL> WITH t(str) AS(
  2  SELECT 'abcdefgh - 1234 (ijklmno)' FROM dual
  3  )
  4  SELECT substr(str, 12, 4) new_str FROM t;

NEW_
----
1234

SQL>

你可以用第一个表的主键加入substr(str, 12, 4)

SELECT column_list
FROM table_1 t1
JOIN table_2 t2
ON (t1.primary_key = substr(column_name, 12, 4)
WHERE ...

性能角度,您需要在第二个表的列上创建一个基于函数的索引

CREATE INDEX fn_idx_col ON table_2(substr(column_name, 12, 4));

从 11g 开始,引入了VIRTUAL COLUMN。但是,在虚拟列上创建索引与在静态列上创建基于函数的索引相同。

注意由于您没有外键关系,因此您总是会得到两个表上的表扫描。如果您有正确的关系,那么理想情况下 Oracle 将只使用第一个表的主键并避免对第二个表进行任何表扫描

【讨论】:

  • 也可以根据虚拟列创建外键约束,没问题。
  • @Wernfried 是的,但是 OP 在 10g 上。所以,没有深入了解它。
【解决方案3】:

使用regexp_replace。试试这个查询

select * from table1
where table1.ID in (select regexp_replace(column2, '[A-Za-z]') from table2)

regexp_replace 的语法是
REGEXP_REPLACE(string, target [, replacement [, position [, occurrence [, regexp_modifiers]]]])

【讨论】:

  • 事实上我使用 regexp_substr 代替 regexp_replace 如果有人会遇到类似的问题,但你的回答让我找到了解决方案。
  • 使用 regexp_substr 真是个好主意。让我编辑我的答案
猜你喜欢
  • 2020-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-11
  • 1970-01-01
相关资源
最近更新 更多