1

シンプルで汎用的なテーブル構造を PostgreSQL に実装しています (8.3; 9.1 は近未来です)。これは非常に単純で一般的な実装のようです。要約すると、次のようになります。

events_event_types
(
    # this table holds some 50 rows
    id bigserial # PK
    "name" character varying(255)
)

events_events
(
    # this table holds some 15M rows
    id bigserial # PK
    datetime timestamp with time zone
    eventtype_id bigint # FK to events_event_types.id
)

CREATE TABLE events_eventdetails
(
    # this table holds some 65M rows
    id bigserial # PK
    keyname character varying(255)
    "value" text
    event_id bigint # FK to events_events.id
)

events_events および events_eventdetails テーブルの一部の行は次のようになります。

events_events               | events_eventdetails
  id  datetime eventtype_id |   id   keyname       value        event_id
----------------------------|-------------------------------------------
  100 ...      10           |   1000 transactionId 9774ae16-... 100
                            |   1001 someKey       some value   100
  200 ...      20           |   2000 transactionId 9774ae16-... 200
                            |   2001 reductionId   123          200
                            |   2002 reductionId   456          200
  300 ...      30           |   3000 transactionId 9774ae16-... 300
                            |   2001 customerId    234          300
                            |   2001 companyId     345          300

events_events 行 100 と 200 と 300 を 1 つの結果セットにまとめて高速で返す「ソリューション」がどうしても必要です。reductionId=123 を要求されたとき、customerId=234 を要求されたとき、または companyId=345 を要求されたとき。(これらの基準の AND の組み合わせに興味があるかもしれませんが、それが本質的な目標ではありません。) この時点で問題があるかどうかはわかりませんが、結果セットは datetime 範囲と eventtype_id (IN リスト) でフィルター可能であり、LIMIT が与えられる必要があります。

これは次のいずれかである可能性があるため、「解決策」を求めます。

  • 単一のクエリ
  • 2 つの小さなクエリ (中間結果が常に十分に小さい限り。私はこのアプローチに従い、大量 (~20k) の関連付けられたトランザクション (transactionId) を持つ企業 (companyId) で行き詰まりました)
  • 微妙な再設計 (非正規化など)

これは新しい質問ではありません。私たちは何ヶ月にもわたって 3 つのアプローチすべてを試してきたので (これらのクエリで煩わされることはありません)、パフォーマンスではすべて失敗します。解は <<<1 秒で返されます。以前の試みは約かかりました。せいぜい10秒。

私は本当に助けていただければ幸いです-私は今途方に暮れています...


2 つの小さなクエリ アプローチは、次のようになります。

クエリ 1:

SELECT Substring(details2_transvalue.VALUE, 0, 32)
    FROM events_eventdetails details2_transvalue
    JOIN events_eventdetails compdetails ON details2_transvalue.event_id = compdetails.event_id
    AND compdetails.keyname = 'companyId'
    AND Substring(compdetails.VALUE, 0, 32) = '4'
    AND details2_transvalue.keyname = 'transactionId'

クエリ 2:

SELECT events1.*
    FROM events_events events1
    JOIN events_eventdetails compDetails ON events1.id = compDetails.event_id
    AND compDetails.keyname='companyId'
    AND substring(compDetails.value,0,32)='4'
    WHERE events1.eventtype_id IN (...)
UNION
SELECT events2.*
    FROM events_events events2
    JOIN events_eventdetails details2_transKey ON events2.id = details2_transKey.event_id
    AND details2_transKey.keyname='transactionId'
    AND substring(details2_transKey.value,0,32) IN ( -- result of query 1 goes here -- )
    WHERE events2.eventtype_id IN (...)
    ORDER BY dateTime DESC LIMIT 50

クエリ 1 で返されるセットが大きいため、このパフォーマンスは低下します。

ご覧のとおり、events_eventdetails テーブルの値は常に長さ 32 の部分文字列として表現され、そのようにインデックスが付けられています。keyname、event_id、event_id + keyname、keyname + 長さ 32 の部分文字列の追加のインデックス。


これはPostgreSQL 9.1のアプローチです-私は公式にそのプラットフォームを自由に使えるわけではありませんが:

WITH companyevents AS (
SELECT events1.*
FROM events_events events1
JOIN events_eventdetails compDetails
ON events1.id = compDetails.event_id
AND compDetails.keyname='companyId'
AND substring(compDetails.value,0,32)=' -- my desired companyId -- '
WHERE events1.eventtype_id in (...)
ORDER BY dateTime DESC
LIMIT 50
)
SELECT * from events_events
WHERE transaction_id IN (SELECT transaction_id FROM companyevents)
OR id IN (SELECT id FROM companyevents)
AND eventtype_id IN (...)
ORDER BY dateTime DESC
LIMIT 250;

28228 個のトランザクション ID を持つ companyId のクエリ プランは次のとおりです。

 Limit  (cost=7545.99..7664.33 rows=250 width=130) (actual time=210.100..3026.267 rows=50 loops=1)
   CTE companyevents
     ->  Limit  (cost=7543.62..7543.74 rows=50 width=130) (actual time=206.994..207.020 rows=50 loops=1)
           ->  Sort  (cost=7543.62..7544.69 rows=429 width=130) (actual time=206.993..207.005 rows=50 loops=1)
                 Sort Key: events1.datetime
                 Sort Method: top-N heapsort  Memory: 23kB
                 ->  Nested Loop  (cost=10.02..7529.37 rows=429 width=130) (actual time=0.093..178.719 rows=28228 loops=1)
                       ->  Append  (cost=10.02..1140.62 rows=657 width=8) (actual time=0.082..27.594 rows=28228 loops=1)
                             ->  Bitmap Heap Scan on events_eventdetails compdetails  (cost=10.02..394.47 rows=97 width=8) (actual time=0.021..0.021 rows=0 loops=1)
                                   Recheck Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '4'::text))
                                   ->  Bitmap Index Scan on events_eventdetails_substring_ind  (cost=0.00..10.00 rows=97 width=0) (actual time=0.019..0.019 rows=0 loops=1)
                                         Index Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '4'::text))
                             ->  Index Scan using events_eventdetails_companyid_substring_ind on events_eventdetails_companyid compdetails  (cost=0.00..746.15 rows=560 width=8) (actual time=0.061..18.655 rows=28228 loops=1)
                                   Index Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '4'::text))
                       ->  Index Scan using events_events_pkey on events_events events1  (cost=0.00..9.71 rows=1 width=130) (actual time=0.004..0.004 rows=1 loops=28228)
                             Index Cond: (id = compdetails.event_id)
                             Filter: (eventtype_id = ANY ('{103,106,107,110,45,34,14,87,58,78,7,76,42,11,25,57,98,37,30,35,33,49,52,29,74,28,85,59,51,65,66,18,13,86,75,6,44,38,43,94,56,95,96,71,50,81,90,89,16,17,4,88,79,77,68,97,92,67,72,53,2,10,31,32,80,111,104,93,26,8,61,5,73,70,63,20,60,40,41,23,22,48,36,108,99,64,62,55,69,19,46,47,15,54,100,101,27,21,12,102,105,109,112,113,114,115,116,119,120,121,122,123,124,9,127,24,130,132,129,125,131,118,117,133,134}'::bigint[]))
   ->  Index Scan Backward using events_events_datetime_ind on events_events  (cost=2.25..1337132.75 rows=2824764 width=130) (actual time=210.100..3026.255 rows=50 loops=1)
         Filter: ((hashed SubPlan 2) OR ((hashed SubPlan 3) AND (eventtype_id = ANY ('{103,106,107,110,45,34,14,87,58,78,7,76,42,11,25,57,98,37,30,35,33,49,52,29,74,28,85,59,51,65,66,18,13,86,75,6,44,38,43,94,56,95,96,71,50,81,90,89,16,17,4,88,79,77,68,97,92,67,72,53,2,10,31,32,80,111,104,93,26,8,61,5,73,70,63,20,60,40,41,23,22,48,36,108,99,64,62,55,69,19,46,47,15,54,100,101,27,21,12,102,105,109,112,113,114,115,116,119,120,121,122,123,124,9,127,24,130,132,129,125,131,118,117,133,134}'::bigint[]))))
         SubPlan 2
           ->  CTE Scan on companyevents  (cost=0.00..1.00 rows=50 width=90) (actual time=206.998..207.071 rows=50 loops=1)
         SubPlan 3
           ->  CTE Scan on companyevents  (cost=0.00..1.00 rows=50 width=8) (actual time=0.001..0.026 rows=50 loops=1)
 Total runtime: 3026.410 ms

288 個のトランザクション ID を持つ companyId のクエリ プランは次のとおりです。

 Limit  (cost=7545.99..7664.33 rows=250 width=130) (actual time=30.976..3790.362 rows=54 loops=1)
   CTE companyevents
     ->  Limit  (cost=7543.62..7543.74 rows=50 width=130) (actual time=9.263..9.290 rows=50 loops=1)
           ->  Sort  (cost=7543.62..7544.69 rows=429 width=130) (actual time=9.263..9.272 rows=50 loops=1)
                 Sort Key: events1.datetime
                 Sort Method: top-N heapsort  Memory: 24kB
                 ->  Nested Loop  (cost=10.02..7529.37 rows=429 width=130) (actual time=0.071..8.195 rows=1025 loops=1)
                       ->  Append  (cost=10.02..1140.62 rows=657 width=8) (actual time=0.060..1.348 rows=1025 loops=1)
                             ->  Bitmap Heap Scan on events_eventdetails compdetails  (cost=10.02..394.47 rows=97 width=8) (actual time=0.021..0.021 rows=0 loops=1)
                                   Recheck Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '5'::text))
                                   ->  Bitmap Index Scan on events_eventdetails_substring_ind  (cost=0.00..10.00 rows=97 width=0) (actual time=0.019..0.019 rows=0 loops=1)
                                         Index Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '5'::text))
                             ->  Index Scan using events_eventdetails_companyid_substring_ind on events_eventdetails_companyid compdetails  (cost=0.00..746.15 rows=560 width=8) (actual time=0.039..1.006 rows=1025 loops=1)
                                   Index Cond: (((keyname)::text = 'companyId'::text) AND ("substring"(value, 0, 32) = '5'::text))
                       ->  Index Scan using events_events_pkey on events_events events1  (cost=0.00..9.71 rows=1 width=130) (actual time=0.005..0.006 rows=1 loops=1025)
                             Index Cond: (id = compdetails.event_id)
                             Filter: (eventtype_id = ANY ('{103,106,107,110,45,34,14,87,58,78,7,76,42,11,25,57,98,37,30,35,33,49,52,29,74,28,85,59,51,65,66,18,13,86,75,6,44,38,43,94,56,95,96,71,50,81,90,89,16,17,4,88,79,77,68,97,92,67,72,53,2,10,31,32,80,111,104,93,26,8,61,5,73,70,63,20,60,40,41,23,22,48,36,108,99,64,62,55,69,19,46,47,15,54,100,101,27,21,12,102,105,109,112,113,114,115,116,119,120,121,122,123,124,9,127,24,130,132,129,125,131,118,117,133,134}'::bigint[]))
   ->  Index Scan Backward using events_events_datetime_ind on events_events  (cost=2.25..1337132.75 rows=2824764 width=130) (actual time=30.975..3790.332 rows=54 loops=1)
         Filter: ((hashed SubPlan 2) OR ((hashed SubPlan 3) AND (eventtype_id = ANY ('{103,106,107,110,45,34,14,87,58,78,7,76,42,11,25,57,98,37,30,35,33,49,52,29,74,28,85,59,51,65,66,18,13,86,75,6,44,38,43,94,56,95,96,71,50,81,90,89,16,17,4,88,79,77,68,97,92,67,72,53,2,10,31,32,80,111,104,93,26,8,61,5,73,70,63,20,60,40,41,23,22,48,36,108,99,64,62,55,69,19,46,47,15,54,100,101,27,21,12,102,105,109,112,113,114,115,116,119,120,121,122,123,124,9,127,24,130,132,129,125,131,118,117,133,134}'::bigint[]))))
         SubPlan 2
           ->  CTE Scan on companyevents  (cost=0.00..1.00 rows=50 width=90) (actual time=9.266..9.327 rows=50 loops=1)
         SubPlan 3
           ->  CTE Scan on companyevents  (cost=0.00..1.00 rows=50 width=8) (actual time=0.001..0.019 rows=50 loops=1)
 Total runtime: 3796.736 ms

3 秒/4 秒の場合、これはまったく悪くありませんが、それでも 100 倍以上遅すぎます。また、これは関連するハードウェアにはありませんでした。それにもかかわらず、痛みがどこにあるかを示す必要があります。


解決策に発展する可能性のあるものを次に示します。

表を追加しました:

events_transaction_helper
(
    event_id bigint not null
    transactionid character varying(36) not null
    keyname character varying(255) not null
    value bigint not null
    # index on keyname, value
)

私は今、このテーブルを「手動で」入力しましたが、具体化されたビューの実装がそのトリックを行います。以下のクエリによく従います。

SELECT tr.event_id, tr.value AS transactionid, det.keyname, det.value AS value
   FROM events_eventdetails tr
   JOIN events_eventdetails det ON det.event_id = tr.event_id
  WHERE tr.keyname = 'transactionId'
     AND det.keyname
     IN ('companyId', 'reduction_id', 'customer_id');

events_events テーブルに次の列を追加しました。

transaction_id character varying(36) null

この新しい列は次のように入力されます。

update events_events
set transaction_id =
    (select value from events_eventdetails
        where keyname='transactionId'
        and event_id=events_events.id);

現在、次のクエリは一貫して 15 ミリ秒未満で返されます。

explain analyze select * from events_events
    where transactionId in
    (select distinct transactionid
        from events_transaction_helper
        WHERE keyname='companyId' and value=5)
    and eventtype_id in (...)
    order by datetime desc limit 250;

 Limit  (cost=5075.23..5075.85 rows=250 width=130) (actual time=8.901..9.028 rows=250 loops=1)
   ->  Sort  (cost=5075.23..5077.19 rows=785 width=130) (actual time=8.900..8.953 rows=250 loops=1)
         Sort Key: events_events.datetime
         Sort Method: top-N heapsort  Memory: 81kB
         ->  Nested Loop  (cost=57.95..5040.04 rows=785 width=130) (actual time=0.928..8.268 rows=524 loops=1)
               ->  HashAggregate  (cost=52.30..52.42 rows=12 width=37) (actual time=0.895..0.991 rows=276 loops=1)
                     ->  Subquery Scan on "ANY_subquery"  (cost=52.03..52.27 rows=12 width=37) (actual time=0.558..0.757 rows=276 loops=1)
                           ->  HashAggregate  (cost=52.03..52.15 rows=12 width=37) (actual time=0.556..0.638 rows=276 loops=1)
                                 ->  Index Scan using testmaterializedviewkeynamevalue on events_transaction_helper  (cost=0.00..51.98 rows=22 width=37) (actual time=0.068..0.404 rows=288 loops=1)
                                       Index Cond: (((keyname)::text = 'companyId'::text) AND (value = 5))
               ->  Bitmap Heap Scan on events_events  (cost=5.65..414.38 rows=100 width=130) (actual time=0.023..0.024 rows=2 loops=276)
                     Recheck Cond: ((transactionid)::text = ("ANY_subquery".transactionid)::text)
                     Filter: (eventtype_id = ANY ('{103,106,107,110,45,34,14,87,58,78,7,76,42,11,25,57,98,37,30,35,33,49,52,29,74,28,85,59,51,65,66,18,13,86,75,6,44,38,43,94,56,95,96,71,50,81,90,89,16,17,4,88,79,77,68,97,92,67,72,53,2,10,31,32,80,111,104,93,26,8,61,5,73,70,63,20,60,40,41,23,22,48,36,108,99,64,62,55,69,19,46,47,15,54,100,101,27,21,12,102,105,109,112,113,114,115,116,119,120,121,122,123,124,9,127,24,130,132,129,125,131,118,117,133,134}'::bigint[]))
                     ->  Bitmap Index Scan on testtransactionid  (cost=0.00..5.63 rows=100 width=0) (actual time=0.020..0.020 rows=2 loops=276)
                           Index Cond: ((transactionid)::text = ("ANY_subquery".transactionid)::text)
 Total runtime: 9.122 ms

後で確認して、これが実際に実行可能な解決策であるかどうかをお知らせします:)

4

3 に答える 3

1

アイデアは、非正規化ではなく、正規することです。events_details() テーブルは 2 つのテーブルで置き換えることができます。1 つは event_detail_types を含み、もう 1 つは実際の値 ({even_id,detail_types} を参照) を含みます。これにより、クエリの実行が容易になります。すべてのキー名を 1 回だけ保存して取得し、比較するだけでよいため、DBMS が取得する必要のあるページ数が減ります。

注:名前を少し変更しました。ほとんどの場合、正気と安全のためです。

SET search_path='cav';
/**** ***/
DROP SCHEMA cav CASCADE;
CREATE SCHEMA cav;
SET search_path='cav';

CREATE TABLE event_types
(
    -- this table holds some 50 rows
    id bigserial PRIMARY KEY
    , zname varchar(255)
);
INSERT INTO event_types(zname)
SELECT 'event_'::text || gs::text
FROM generate_series (1,100) gs
        ;

CREATE TABLE events
(
    -- this table holds some 15M rows
    id bigserial PRIMARY KEY
    , zdatetime timestamp with time zone
    , eventtype_id bigint REFERENCES event_types(id)
);
INSERT INTO events(zdatetime,eventtype_id)
SELECT gs, et.id
FROM generate_series ('2012-04-11 00:00:00'::timestamp
                     , '2012-04-12 12:00:00'::timestamp  ,' 1 hour'::interval ) gs
        , event_types et
        ;

-- SELECT * FROM event_types;
-- SELECT * FROM events;

CREATE TABLE event_details
(
    -- this table holds some 65M rows
    id bigserial PRIMARY KEY
    , event_id bigint REFERENCES events(id)
    , keyname varchar(255)
    , zvalue text
);

INSERT INTO event_details(event_id, keyname)
SELECT ev.id,im.*
FROM events ev
        , (VALUES ('transactionId'::text),('someKey'::text)
           ,('reductionId'::text),('customerId'::text),('companyId'::text)
          ) im
        ;
UPDATE event_details
SET zvalue = 'Some_value'::text || (random() * 1000)::int::text
        ;
        --
        -- Domain table with all valid detail_types
        --
CREATE TABLE detail_types(
        id bigserial PRIMARY KEY
        , keyname varchar(255)
        );
INSERT INTO detail_types(keyname)
SELECT DISTINCT keyname
        FROM event_details
        ;

        --
        -- Context-attribute-value table, referencing {event_id, type_id}
        --
CREATE TABLE event_detail_values
        ( event_id BIGINT
        , detail_type_id BIGINT
        , zvalue text
        , PRIMARY KEY(event_id , detail_type_id)
        , FOREIGN KEY(event_id ) REFERENCES events(id)
        , FOREIGN KEY(detail_type_id)REFERENCES detail_types(id)
        );

        --
        -- For the sake of joining we create some natural keys
        --
CREATE INDEX events_details_keyname ON event_details (keyname) ;
CREATE INDEX detail_types_keyname ON detail_types(keyname) ;

INSERT INTO event_detail_values (event_id,detail_type_id, zvalue)
        SELECT ed.event_id, dt.id
                , ed.zvalue
        FROM event_details ed
        , detail_types dt
        WHERE ed.keyname = dt.keyname
        ;
        --
        -- Now we can drop the original table, and use the view instead
        --
DROP TABLE event_details;
CREATE VIEW event_details AS (
        SELECT dv.event_id AS event_id
                , dt.keyname AS keyname
                , dv.zvalue AS zvalue
        FROM event_detail_values dv
        JOIN detail_types dt ON dt.id = dv.detail_type_id
        );
EXPLAIN ANALYZE
SELECT ev.id AS event_id
        , ev.zdatetime AS zdatetime
        , ed.keyname AS keyname
        , ed.zvalue AS zevalue
        FROM events ev
        JOIN event_details ed ON ed.event_id = ev.id
        WHERE ed.keyname IN ('transactionId','customerId','companyId')
        ORDER BY event_id,keyname
        ;

結果のクエリ プラン:

                                                                 QUERY PLAN                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=1178.79..1197.29 rows=7400 width=40) (actual time=159.902..177.379 rows=11100 loops=1)
   Sort Key: ev.id, dt.keyname
   Sort Method: external sort  Disk: 560kB
   ->  Hash Join  (cost=108.34..703.22 rows=7400 width=40) (actual time=12.225..122.231 rows=11100 loops=1)
         Hash Cond: (dv.event_id = ev.id)
         ->  Hash Join  (cost=1.09..466.47 rows=7400 width=32) (actual time=0.047..74.183 rows=11100 loops=1)
               Hash Cond: (dv.detail_type_id = dt.id)
               ->  Seq Scan on event_detail_values dv  (cost=0.00..322.00 rows=18500 width=29) (actual time=0.006..26.543 rows=18500 loops=1)
               ->  Hash  (cost=1.07..1.07 rows=2 width=19) (actual time=0.025..0.025 rows=3 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 1kB
                     ->  Seq Scan on detail_types dt  (cost=0.00..1.07 rows=2 width=19) (actual time=0.009..0.014 rows=3 loops=1)
                           Filter: ((keyname)::text = ANY ('{transactionId,customerId,companyId}'::text[]))
         ->  Hash  (cost=61.00..61.00 rows=3700 width=16) (actual time=12.161..12.161 rows=3700 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 131kB
               ->  Seq Scan on events ev  (cost=0.00..61.00 rows=3700 width=16) (actual time=0.004..5.926 rows=3700 loops=1)
 Total runtime: 192.724 ms
 (16 rows)

ご覧のとおり、クエリの「最も深い」部分は、文字列のリストを指定して、detail_type_ids を取得することです。これはハッシュ テーブルに入れられ、detail_values の対応するハッシュセットと結合されます。(注: これは pg-9.1 です)

YMMV。

于 2012-04-12T11:14:14.760 に答える
0

これらの方針に沿った設計を使用する必要がある場合は、events_eventdetails から id 列を削除し、主キーを (event_id, keyname) と宣言する必要があります。これにより、合成キーの役に立たないインデックスを維持することなく、非常に便利なインデックスが得られます。

events_eventdetails テーブルを完全に削除し、GIN インデックスを使用して、そのデータに hstore 列を使用することをお勧めします。これにより、保存するイベントの詳細を事前に定義しなくても、パフォーマンスの目標を達成できる可能性があります。

さらに良いのは、可能なイベントの詳細を予測または指定できる場合は、データベース内にデータベースを実装しようとしないことです。各「キー名」値を、そのデータの性質に適したデータ型で events_eventdetails の列にします。ALTER TABLEこれにより、詳細の性質が変化するにつれてステートメントを発行する必要があるという犠牲を払って、おそらくはるかに高速なアクセスが可能になります。

于 2012-04-10T23:41:31.863 に答える
0

キー (reductionIdこの場合) がテーブル内のすべての行の 7 ~ 10% 以上でevents_eventdetails一致する場合、PostgreSQL は SeqScan を優先します。あなたにできることは何もありません、それが最速の方法です。

ISO8583 パケットで同様のケースがありました。各パケットは (設計上) 128 フィールドで構成されているため、最初のデータベース設計では 2 つのテーブルを使用したアプローチに従いました。

  • field_idおよび1つの表の説明(events_eventsあなたの場合)、
  • field_id+field_value別の ( events_eventdetails)。

このようなレイアウトは 3NF に従いますが、同じ問題がすぐに発生します。

  • 悪いパフォーマンス、
  • 非常に複雑なクエリ。

あなたの場合、再設計する必要があります。1 つのオプション (より簡単な方法) を にするevents_eventdetails.keynameことsmallintです。これにより、比較操作が高速になります。大勝利ではないけど。

別のオプションは、次のように、2 つのテーブルを 1 つのテーブルに減らすことです。

CREATE TABLE events_events (
    id            bigserial,
    datetime      timestamp with time zone,
    eventtype_id  bigint,
    transactionId text,   -- value for transactionId 
    reductionId   text,   --   -"-     reductionId
    companyId     text,   -- etc.
    customerId    text,
    anyotherId    text,
    ...
);

これは 3NF壊しますが、一方で:

  • データのインデックス作成の自由度が高まります。
  • クエリが短くなり、保守が容易になります。
  • パフォーマンスが大幅に向上します。

考えられる欠点:

  • 未使用のフィールドにもう少しスペースを浪費します:unused fields / 8行あたりのバイト数
  • 別の列を保持するには遅すぎるイベント用に追加のテーブルが必要になる場合があります。

編集:

ここで具現化するという意味がよくわかりません。

あなたの質問で、あなたが欲しいと言った:

events_events 行 100 と 200 と 300 を 1 つの結果セットと FAST で一緒に返す「ソリューション」! reductionId=123 を要求されたとき、customerId=234 を要求されたとき、または companyId=345 を要求されたとき。

提案された再設計により、クロスタブまたはピボット テーブルが から作成されますevents_eventdetails。そして、条件を満たすすべての events_events 行を取得するには、次を使用できます。

SELECT *
  FROM events_events
 WHERE id IN (100, 200, 300)
   AND reductionId = 123
-- AND customerId = 234
-- AND companyId = 345;
于 2012-04-11T06:13:42.887 に答える