تحسين استعلام التاريخ لجداول الأطفال الكبيرة: gist أو الجن؟

StackOverflow https://stackoverflow.com/questions/2871057

سؤال

مشكلة

72 طاولات الأطفال ، كل منها له فهرس عام ومؤشر محطة ، يتم تعريفه على النحو التالي:

CREATE TABLE climate.measurement_12_013
(
-- Inherited from table climate.measurement_12_013:  id bigint NOT NULL DEFAULT nextval('climate.measurement_id_seq'::regclass),
-- Inherited from table climate.measurement_12_013:  station_id integer NOT NULL,
-- Inherited from table climate.measurement_12_013:  taken date NOT NULL,
-- Inherited from table climate.measurement_12_013:  amount numeric(8,2) NOT NULL,
-- Inherited from table climate.measurement_12_013:  category_id smallint NOT NULL,
-- Inherited from table climate.measurement_12_013:  flag character varying(1) NOT NULL DEFAULT ' '::character varying,
  CONSTRAINT measurement_12_013_category_id_check CHECK (category_id = 7),
  CONSTRAINT measurement_12_013_taken_check CHECK (date_part('month'::text, taken)::integer = 12)
)
INHERITS (climate.measurement)

CREATE INDEX measurement_12_013_s_idx
  ON climate.measurement_12_013
  USING btree
  (station_id);
CREATE INDEX measurement_12_013_y_idx
  ON climate.measurement_12_013
  USING btree
  (date_part('year'::text, taken));

(قيود المفاتيح الخارجية لإضافتها لاحقًا.)

يركض الاستعلام التالي بطيئًا بسبب مسح الجدول الكامل:

SELECT
  count(1) AS measurements,
  avg(m.amount) AS amount
FROM
  climate.measurement m
WHERE
  m.station_id IN (
    SELECT
      s.id
    FROM
      climate.station s,
      climate.city c
    WHERE
        /* For one city... */
        c.id = 5182 AND

        /* Where stations are within an elevation range... */
        s.elevation BETWEEN 0 AND 3000 AND

        /* and within a specific radius... */
        6371.009 * SQRT( 
          POW(RADIANS(c.latitude_decimal - s.latitude_decimal), 2) +
            (COS(RADIANS(c.latitude_decimal + s.latitude_decimal) / 2) *
              POW(RADIANS(c.longitude_decimal - s.longitude_decimal), 2))
        ) <= 50
    ) AND

  /* Data before 1900 is shaky; insufficient after 2009. */
  extract( YEAR FROM m.taken ) BETWEEN 1900 AND 2009 AND

  /* Whittled down by category... */
  m.category_id = 1 AND

  /* Between the selected days and years... */
  m.taken BETWEEN
   /* Start date. */
   (extract( YEAR FROM m.taken )||'-01-01')::date AND
    /* End date. Calculated by checking to see if the end date wraps
       into the next year. If it does, then add 1 to the current year.
    */
    (cast(extract( YEAR FROM m.taken ) + greatest( -1 *
      sign(
        (extract( YEAR FROM m.taken )||'-12-31')::date -
        (extract( YEAR FROM m.taken )||'-01-01')::date ), 0
    ) AS text)||'-12-31')::date
GROUP BY
  extract( YEAR FROM m.taken )

يأتي البطيخ من هذا الجزء من الاستعلام:

  m.taken BETWEEN
    /* Start date. */
  (extract( YEAR FROM m.taken )||'-01-01')::date AND
    /* End date. Calculated by checking to see if the end date wraps
      into the next year. If it does, then add 1 to the current year.
    */
    (cast(extract( YEAR FROM m.taken ) + greatest( -1 *
      sign(
        (extract( YEAR FROM m.taken )||'-12-31')::date -
        (extract( YEAR FROM m.taken )||'-01-01')::date ), 0
    ) AS text)||'-12-31')::date

هذا الجزء من الاستعلام يطابق مجموعة مختارة من الأيام. على سبيل المثال ، إذا أراد المستخدم النظر في البيانات بين 1 يونيو 1st و 1 يوليو على مدار السنوات التي توجد فيها بيانات ، فإن البند أعلاه يتطابق مع تلك الأيام فقط. إذا أراد الاستخدام أن ينظر إلى البيانات بين 22 ديسمبر و 22 مارس ، مرة أخرى لجميع السنوات التي توجد بها بيانات ، فإن البند أعلاه يحسب أن 22 مارس في العام التالي من 22 ديسمبر ، وبالتالي يتطابق مع التاريخ وفقًا لذلك:

يتم إصلاح التواريخ حاليًا في 1 يناير إلى 31 ديسمبر ، ولكن سيتم تحديدها ، كما هو موضح أعلاه.

يُظهر hashaggregate من الخطة تكلفة 10006220141.11 ، وهو ، كما أظن ، على الجانب الضخم الفلكي.

يوجد فحص كامل للجدول على جدول القياس (نفسه ليس له بيانات ولا فهارس). يتجمع الجدول 273 مليون صف من طاولات أطفاله.

سؤال

ما هي الطريقة الصحيحة لفهرسة التواريخ لتجنب عمليات مسح الجدول الكاملة؟

الخيارات التي فكرت فيها:

  • شرك
  • جوهر
  • أعد كتابة البند حيث
  • منفصل عن الأعمدة على الجداول ، الشهر

ما رأيك؟

شكرًا لك!

هل كانت مفيدة؟

المحلول

مشكلتك هي أن لديك شرط حيث يعتمد على حساب التاريخ. لا توجد طريقة يمكن أن تستخدم قاعدة البيانات فهرسًا إذا احتاجت إلى جلب كل صف والقيام بحساب عليه قبل معرفة ما إذا كان التاريخ سوف يتطابق.

ما لم تقم بإعادة كتابته ليكون في النموذج الذي تحتوي فيه قاعدة البيانات على نطاق ثابت للتحقق مما لا يعتمد على البيانات ، فسيتعين عليك دائمًا مسح الجدول.

نصائح أخرى

جرب شيئًا كهذا:

create temporary table test (d date);

insert into test select '1970-01-01'::date+generate_series(1,50*365);

analyze test

create function month_day(d date) returns int as $$
  select extract(month from $1)::int*100+extract(day from $1)::int $$
language sql immutable strict;

create index test_d_month_day_idx on test (month_day(d));

explain analyze select * from test
  where month_day(d)>=month_day('2000-04-01')
  and month_day(d)<=month_day('2000-04-05');

أعتقد أن تشغيل هذا بكفاءة عبر تلك الأقسام ، وسأكون تطبيقك أكثر ذكاءً حول نطاقات التاريخ. اطلب من ذلك إنشاء قائمة فعلية من التواريخ للتحقق من كل قسم ، ثم قم بإنشاء استعلام واحد مع اتحاد بين الأقسام. يبدو أن مجموعة البيانات الخاصة بك ثابتة إلى حد ما ، لذلك يمكن أن تحسن مجموعة مؤشر التاريخ بشكل كبير أيضًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top