Postgresql

如何對所有列都需要索引的數據進行建模?

  • April 26, 2017

我有以下案例,其中有一堆相互映射的 ID (GUID)。我需要查找給定任何單個 ID 的所有 ID。例如,假設我的數據具有以下列:

uid, id1, id2, id3, id4, id5

如何支持以下查詢:

1. SELECT uid FROM table WHERE id1=x;
2. SELECT id1, id2, id3, id4 FROM table WHERE uid=xyz;

ID 的數量可以更改(我必須添加新列)。

我可以在所有列上創建索引,但這不是很有效。我應該對這些數據進行不同的建模嗎?是否有任何 NoSQL 數據庫可以幫助為這個案例建模?

為了清晰起見,提供更多業務上下文,我們有來自多個數據源的數據,這些數據源具有自己的 custom_id。但它們都標識同一個人 (UID)。這就是為什麼我們需要將所有數據源 ID 映射到我們自己的內部 UID。因此問題。

  • 列數的最大界限為 20-25。
  • 它們可以為空。
  • 每行的 id 數量可以不同(這就是為什麼可以為空)。
  • 所有的數據類型都是 STRING。
  • UID 將是主鍵。

您可以將表建模為具有兩列的字典:

  1. 使用者標識符
  2. ID_VALUE

兩者都將使用專用索引進行索引。

然後,您可以為任何 UID 添加盡可能多的 ID_VALUE 行並蒐索給定 ID_VALUE 的 UID。

然後查詢看起來像

SELECT UID FROM table WHERE ID_VALUE=x;
SELECT ID_VALUE FROM table WHERE UID=xyz;

第二個查詢將返回不止一行。


編輯

我需要查找給定任何單個 id 的所有 id

只要您的所有 ID 都由一個公共列綁定,在本例中為 UID,這應該是可能的。考慮以下查詢:

SELECT t2.id_value
 FROM table t1
     ,table t2
WHERE t1.uid = t2.uid
  AND t1.id_value = x

這將返回給定單個 id X 的所有 id。

ID_VALUE 的值是多少並不重要。它們可能是指向這個或任何其他任意數字的其他 UID。如果您想搜尋包括 UID 值的任何 ID_VALUE,您也可以將此 UID 值包含到集合中,這樣您將有一行 UID = ID_VALUE。

例子

假設您的模型中有以下數據:

UID   ID1   ID2  ID3
--------------------
 1     2     3    4
 2     1     4
 3     4
 4     2     3

您可以像這樣在字典表中對此數據進行建模:

UID   ID_VALUE
--------------
 1          2
 1          3
 1          4
 2          1
 2          4
 3          4
 4          2
 4          3

如果您想在列表中包含 UID,那麼我範例中的最後一個查詢將返回包括 UID 在內的所有 ID,那麼您將擁有:

UID   ID_VALUE
--------------
 1          2
 1          3
 1          4
 1          1 <---
 2          1
 2          4
 2          2 <---
 3          4
 3          3 <---
 4          2
 4          3
 4          4 <---

我已標記為滿足此要求而添加的行。執行查詢:

SELECT t2.uid
     ,t2.id_value
 FROM table t1
     ,table t2
WHERE t1.uid = t2.uid
  AND t1.id_value = 1

將導致:

UID   ID_VALUE
--------------
 1          2
 1          3
 1          4
 1          1 
 2          1
 2          4
 2          2

這將根據要求返回給定任何單個 ID 的所有 ID。請注意,在 UID = 1 和 UID = 2 的兩個集合中都可以找到 ID_VALUE 為 1,兩者都返回。

這相當於在您的平面設計上執行此查詢:

SELECT * FROM table WHERE UID=1 OR ID1=1 OR ID2=1 OR ID3=1

在這種情況下,數組似乎很合適:

create table t1
(
 uid varchar(50) not null primary key,
 alternate_ids varchar(50)[] 
);

insert into t1 (uid, alternate_ids)
values 
('one', array['eno', 'noe']),
('two', array['owt', 'tow', 'wto']),
('three', null);

查詢表:

SELECT uid 
FROM t1
WHERE 'eno' = any(alternate_ids);

SELECT alternate_ids
FROM t1 
WHERE uid = 'one';

查找包含兩個特定備用 ID 的行:

SELECT uid 
FROM t1
WHERE alternate_ids @> array['owt', 'wto']::varchar[];
uid
---
two

(注意:::varchar[]如果列聲明為 ,則不需要強制轉換text[]

查找包含任何備用 ID 的行:

SELECT uid 
FROM t1
WHERE alternate_ids && array['owt', 'noe'];
uid
---
one
two

該數組可以被索引,這使得使用@>and&&運算符查找備用 ID 非常有效:

我用一百萬行填寫了上表。每個都有 1 到 20 個值的隨機數組。

以下查詢從該表返回大約 10000 行(共 100 萬行)

select *
from t1 
where alternate_ids @> array['42'];

執行計劃是:

Bitmap Heap Scan on t1  (cost=47.13..9624.00 rows=10567 width=115) (actual time=5.872..14.894 rows=10635 loops=1)                   
 Recheck Cond: (alternate_ids @> '{42}'::text[])                                                                                   
 Heap Blocks: exact=8169                                                                                                           
 ->  Bitmap Index Scan on t1_alternate_ids_idx  (cost=0.00..44.48 rows=10567 width=0) (actual time=3.665..3.665 rows=10635 loops=1)
       Index Cond: (alternate_ids @> '{42}'::text[])                                                                               
Planning time: 0.274 ms                                                                                                             
Execution time: 15.411 ms                                                                                                           

如果您不熟悉 Postgres 執行計劃:大約 15 毫秒(毫秒)內從該表中檢索到這 10635 行


然而,一個適當規範化的模型也可以正常工作。它可能會給你更多的靈活性。具有數百萬行的表不是問題。甚至數億,取決於您訪問它們的方式。

引用自:https://dba.stackexchange.com/questions/171606