মাস এবং বছরের কলামগুলি আলাদা করুন, বা দিনের সাথে তারিখ সর্বদা 1 তে সেট করা থাকে?


15

আমি Postgres সঙ্গে একটি ডাটাবেস যেখানে দ্বারা জিনিস গোষ্ঠীবদ্ধ অনেকটা হতে সেখানে যাচ্ছে নির্মাণ করছি monthএবং yearদ্বারা, কিন্তু কখনও date

  • আমি পূর্ণসংখ্যা monthএবং yearকলাম তৈরি করতে এবং সেগুলি ব্যবহার করতে পারি।
  • অথবা আমার একটি month_yearকলাম থাকতে পারে এবং সর্বদা day1 এ সেট করা যায় ।

যদি কেউ ডেটা দেখছে তবে প্রাক্তনটিকে কিছুটা সহজ এবং পরিষ্কার মনে হচ্ছে তবে এটি সঠিক টাইপ ব্যবহার করে তবে এটি খুব সুন্দর।


1
অথবা আপনি নিজের ডেটা টাইপ তৈরি করতে পারেন monthযাতে দুটি পূর্ণসংখ্যা থাকে। তবে আমি মনে করি আপনি যদি কখনও কখনও মাসের দিনের প্রয়োজন না হয় তবে দুটি পূর্ণসংখ্যার ব্যবহার করা সম্ভবত সহজতর
a_horse_with_no_name

1
আপনার তারিখের সম্ভাব্য পরিসীমা, সারিগুলির সম্ভাব্য সংখ্যা, আপনি কী অনুকূলকরণের চেষ্টা করছেন (স্টোরেজ, কার্য সম্পাদন, সুরক্ষা, সরলতা?) ​​এবং (সর্বদা বরাবর) আপনার পোস্টগ্রিসের সংস্করণটি ঘোষণা করা উচিত।
ইরউইন ব্র্যান্ডসেটেটার

উত্তর:


17

ব্যক্তিগতভাবে এটি তারিখ হয় বা তারিখ হতে পারে, আমি সর্বদা এটি হিসাবে এক হিসাবে সংরক্ষণ করার পরামর্শ দিই। থাম্বের নিয়ম হিসাবে কাজ করা আরও সহজ।

  • একটি তারিখ 4 বাইট।
  • একটি ছোট্ট 2 বাইট (আমাদের দুটি প্রয়োজন)
    • ... 2 বাইট: বছরের জন্য একটি ছোট
    • ... 2 বাইট: এক মাসের জন্য একটি ছোট

আপনার কাছে এমন একটি তারিখ থাকতে পারে যা আপনাকে কখনই প্রয়োজন হলে দিনটিকে সমর্থন করবে অথবা এক smallintবছর এবং মাসের জন্য এমন একটি দিন যা অতিরিক্ত নির্ভুলতার পক্ষে কখনও সমর্থন করবে না।

নমুনা তথ্য

আসুন এখন একটি উদাহরণ তাকান .. আসুন আমাদের নমুনা জন্য 1 মিলিয়ন তারিখ তৈরি করুন। এটি 1901 এবং 2100 এর মধ্যে 200 বছরের জন্য প্রায় 5000 সারি Every প্রতিবছর প্রতি মাসের জন্য কিছু থাকা উচিত।

CREATE TABLE foo
AS
  SELECT
    x,
    make_date(year,month,1)::date AS date,
    year::smallint,
    month::smallint
  FROM generate_series(1,1e6) AS gs(x)
  CROSS JOIN LATERAL CAST(trunc(random()*12+1+x-x) AS int) AS month
  CROSS JOIN LATERAL CAST(trunc(random()*200+1901+x-x) AS int) AS year
;
CREATE INDEX ON foo(date);
CREATE INDEX ON foo (year,month);
VACUUM FULL ANALYZE foo;

পরীক্ষামূলক

সহজ WHERE

এখন আমরা তারিখটি ব্যবহার না করার এই তত্ত্বগুলি পরীক্ষা করতে পারি .. আমি এই কয়েকটি বার কয়েকবার চালিয়েছি যাতে জিনিস গরম করতে পারে।

EXPLAIN ANALYZE SELECT * FROM foo WHERE date = '2014-1-1'
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=11.56..1265.16 rows=405 width=14) (actual time=0.164..0.751 rows=454 loops=1)
   Recheck Cond: (date = '2014-04-01'::date)
   Heap Blocks: exact=439
   ->  Bitmap Index Scan on foo_date_idx  (cost=0.00..11.46 rows=405 width=0) (actual time=0.090..0.090 rows=454 loops=1)
         Index Cond: (date = '2014-04-01'::date)
 Planning time: 0.090 ms
 Execution time: 0.795 ms

এখন, আসুন তাদের সাথে পৃথক করে অন্য পদ্ধতিটি চেষ্টা করি

EXPLAIN ANALYZE SELECT * FROM foo WHERE year = 2014 AND month = 1;
                                                           QUERY PLAN                                                           
--------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=12.75..1312.06 rows=422 width=14) (actual time=0.139..0.707 rows=379 loops=1)
   Recheck Cond: ((year = 2014) AND (month = 1))
   Heap Blocks: exact=362
   ->  Bitmap Index Scan on foo_year_month_idx  (cost=0.00..12.64 rows=422 width=0) (actual time=0.079..0.079 rows=379 loops=1)
         Index Cond: ((year = 2014) AND (month = 1))
 Planning time: 0.086 ms
 Execution time: 0.749 ms
(7 rows)

নিখরচায়, তারা সব 0.749 নয় .. কিছু কিছু বেশি বা কম, তবে এতে কিছু আসে যায় না। তারা সব তুলনামূলকভাবে এক। এটি সহজভাবে প্রয়োজন হয় না।

এক মাসের মধ্যে

এখন, আসুন আমরা এটির সাথে মজা করি .. আসুন যাক আপনি যোন 2014 এর 1 মাসের মধ্যে (একই মাসে আমরা উপরে ব্যবহার করেছি) সমস্ত অন্তর খুঁজে পেতে চাই।

EXPLAIN ANALYZE
  SELECT *
  FROM foo
  WHERE date
    BETWEEN
      ('2014-1-1'::date - '1 month'::interval)::date 
      AND ('2014-1-1'::date + '1 month'::interval)::date;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=21.27..2310.97 rows=863 width=14) (actual time=0.384..1.644 rows=1226 loops=1)
   Recheck Cond: ((date >= '2013-12-01'::date) AND (date <= '2014-02-01'::date))
   Heap Blocks: exact=1083
   ->  Bitmap Index Scan on foo_date_idx  (cost=0.00..21.06 rows=863 width=0) (actual time=0.208..0.208 rows=1226 loops=1)
         Index Cond: ((date >= '2013-12-01'::date) AND (date <= '2014-02-01'::date))
 Planning time: 0.104 ms
 Execution time: 1.727 ms
(7 rows)

সম্মিলিত পদ্ধতির সাথে এটি তুলনা করুন

EXPLAIN ANALYZE
  SELECT *
  FROM foo
  WHERE year = 2013 AND month = 12
    OR ( year = 2014 AND ( month = 1 OR month = 2) );

                                                                 QUERY PLAN                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=38.79..2999.66 rows=1203 width=14) (actual time=0.664..2.291 rows=1226 loops=1)
   Recheck Cond: (((year = 2013) AND (month = 12)) OR (((year = 2014) AND (month = 1)) OR ((year = 2014) AND (month = 2))))
   Heap Blocks: exact=1083
   ->  BitmapOr  (cost=38.79..38.79 rows=1237 width=0) (actual time=0.479..0.479 rows=0 loops=1)
         ->  Bitmap Index Scan on foo_year_month_idx  (cost=0.00..12.64 rows=421 width=0) (actual time=0.112..0.112 rows=402 loops=1)
               Index Cond: ((year = 2013) AND (month = 12))
         ->  BitmapOr  (cost=25.60..25.60 rows=816 width=0) (actual time=0.218..0.218 rows=0 loops=1)
               ->  Bitmap Index Scan on foo_year_month_idx  (cost=0.00..12.62 rows=420 width=0) (actual time=0.108..0.108 rows=423 loops=1)
                     Index Cond: ((year = 2014) AND (month = 1))
               ->  Bitmap Index Scan on foo_year_month_idx  (cost=0.00..12.38 rows=395 width=0) (actual time=0.108..0.108 rows=401 loops=1)
                     Index Cond: ((year = 2014) AND (month = 2))
 Planning time: 0.256 ms
 Execution time: 2.421 ms
(13 rows)

এটি উভয় ধীর এবং কুরুচিপূর্ণ।

GROUP BY/ORDER BY

সংযুক্ত পদ্ধতি,

EXPLAIN ANALYZE
  SELECT date, count(*)
  FROM foo
  GROUP BY date
  ORDER BY date;
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=20564.75..20570.75 rows=2400 width=4) (actual time=286.749..286.841 rows=2400 loops=1)
   Sort Key: date
   Sort Method: quicksort  Memory: 209kB
   ->  HashAggregate  (cost=20406.00..20430.00 rows=2400 width=4) (actual time=285.978..286.301 rows=2400 loops=1)
         Group Key: date
         ->  Seq Scan on foo  (cost=0.00..15406.00 rows=1000000 width=4) (actual time=0.012..70.582 rows=1000000 loops=1)
 Planning time: 0.094 ms
 Execution time: 286.971 ms
(8 rows)

এবং আবার যৌগিক পদ্ধতিতে

EXPLAIN ANALYZE
  SELECT year, month, count(*)
  FROM foo
  GROUP BY year, month
  ORDER BY year, month;
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=23064.75..23070.75 rows=2400 width=4) (actual time=336.826..336.908 rows=2400 loops=1)
   Sort Key: year, month
   Sort Method: quicksort  Memory: 209kB
   ->  HashAggregate  (cost=22906.00..22930.00 rows=2400 width=4) (actual time=335.757..336.060 rows=2400 loops=1)
         Group Key: year, month
         ->  Seq Scan on foo  (cost=0.00..15406.00 rows=1000000 width=4) (actual time=0.010..70.468 rows=1000000 loops=1)
 Planning time: 0.098 ms
 Execution time: 337.027 ms
(8 rows)

উপসংহার

সাধারণত, স্মার্ট লোকদের কঠোর পরিশ্রম করতে দিন। ডেটম্যাথ শক্ত, আমার ক্লায়েন্টরা আমাকে যথেষ্ট পরিমাণে বেতন দেয় না। আমি এই পরীক্ষা করতাম। আমি হার্ড কি কখনো এই উপসংহারে আমি চেয়ে ভাল ফলাফল পেতে পারে যে চাপা ছিল date। আমি চেষ্টা বন্ধ করে দিয়েছি।

আপডেট

@a_horse_with_no_name এক মাস পরীক্ষার মধ্যে আমার জন্য প্রস্তাবিত WHERE (year, month) between (2013, 12) and (2014,2)। আমার মতে, শীতল হওয়ার সময় এটি আরও জটিল কোয়েরি এবং লাভ না হলে আমি এড়াতে চাই। হায়, এটি নিকটতম হলেও এটি এখনও ধীর ছিল - যা এই পরীক্ষা থেকে বেশি দূরে নেওয়া away এটি কেবল খুব বেশি কিছু দেয় না।

EXPLAIN ANALYZE
  SELECT *
  FROM foo
  WHERE (year, month) between (2013, 12) and (2014,2);

                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=5287.16..15670.20 rows=248852 width=14) (actual time=0.753..2.157 rows=1226 loops=1)
   Recheck Cond: ((ROW(year, month) >= ROW(2013, 12)) AND (ROW(year, month) <= ROW(2014, 2)))
   Heap Blocks: exact=1083
   ->  Bitmap Index Scan on foo_year_month_idx  (cost=0.00..5224.95 rows=248852 width=0) (actual time=0.550..0.550 rows=1226 loops=1)
         Index Cond: ((ROW(year, month) >= ROW(2013, 12)) AND (ROW(year, month) <= ROW(2014, 2)))
 Planning time: 0.099 ms
 Execution time: 2.249 ms
(7 rows)

4
কিছু অন্যান্য RDBMS (দেখতে পৃষ্ঠা 45 ভিন্ন use-the-index-luke.com/blog/2013-07/... ), Postgres এছাড়াও সম্পূর্ণরূপে সারি মান সূচক এক্সেস সমর্থন করে: stackoverflow.com/a/34291099/939860 কিন্তু যে একটি ব্যাপার একদিকে, আমি পুরোপুরি একমত: dateবেশিরভাগ ক্ষেত্রে যাওয়ার উপায় is
এরউইন ব্র্যান্ডসেটেটার 18

5

ইভান ক্যারল প্রস্তাবিত পদ্ধতির বিকল্প হিসাবে, যা আমি সম্ভবত সেরা বিকল্প হিসাবে বিবেচনা করি, আমি কিছু অনুষ্ঠানে ব্যবহার করেছি (এবং বিশেষত পোস্টগ্র্রেএসকিউএল ব্যবহার করার সময় নয়) কেবলমাত্র একটি year_monthকলাম, টাইপ INTEGER(4 বাইট) হিসাবে গণনা করেছি

 year_month = year * 100 + month

এটি হল, আপনি পূর্ণসংখ্যার দুইটি দশমিক অঙ্ক (অঙ্ক 0, এবং 1 সংখ্যা) এবং মাসটি 2 থেকে 5 এর মধ্যে (বা আরও বেশি, প্রয়োজন হলে) সংকেত দিয়েছিলেন ।

এটি কিছুটা হলেও আপনার নিজের ধরণের এবং অপারেটর তৈরির ক্ষেত্রে কোনও দরিদ্র ব্যক্তির বিকল্প year_month। এটি দুটি সুবিধাগুলি পেয়েছে, বেশিরভাগ ক্ষেত্রে "অভিপ্রায়ের স্পষ্টতা" এবং কিছু স্পেস সেভিং (পোস্টগ্র্যাস এসকিউএল তে নয়, আমার মনে হয়) এবং দুটি স্বতন্ত্র কলাম থাকার বিষয়ে কিছু অসুবিধাও রয়েছে।

আপনি গ্যারান্টি দিতে পারেন যে মানগুলি কেবল একটি যুক্ত করে বৈধ

CHECK ((year_date % 100) BETWEEN 1 AND 12)   /*  % = modulus operator */

আপনি দেখতে এমন একটি WHEREধারা থাকতে পারেন:

year_month BETWEEN 201610 and 201702 

এবং এটি দক্ষতার সাথে কাজ করে ( year_monthঅবশ্যই কলামটি যথাযথভাবে সূচকযুক্ত হয়)।

আপনি year_monthতারিখ সহ এবং একই দক্ষতার সাথে (কমপক্ষে) একইভাবে গ্রুপ করতে পারেন ।

আপনার আলাদা করার প্রয়োজন হলে yearএবং monthগণনাটি সোজা for

month = year_month % 100    -- % is modulus operator
year  = year_month / 100    -- / is integer division 

কী অসুবিধে হয় : আপনি যদি 15 টি মাস যুক্ত করতে চান তবে আপনাকে year_monthগণনা করতে হবে (যদি আমি কোনও ভুল বা তদারকি না করি):

year_month + delta (months) = ...

    /* intermediate calculations */
    year = year_month/100 + delta/12    /* years we had + new years */
           + (year_month % 100 + delta%12) / 12  /* extra months make 1 more year? */
    month = ((year_month%10) + (delta%12) - 1) % 12 + 1

/* final result */
... = year * 100 + month

আপনি যদি সতর্ক না হন তবে এটি ত্রুটি প্রবণ হতে পারে।

আপনি যদি দুই বছরের_মাসের মধ্যে মাসের সংখ্যা পেতে চান তবে আপনাকে কিছু অনুরূপ গণনা করতে হবে। এটি (প্রচুর সরলকরণের সাথে) ডেট পাটিগণিতের সাথে প্রকৃতপক্ষে কী ঘটে তা ইতিমধ্যে সংজ্ঞায়িত ফাংশন এবং অপারেটরগুলির মাধ্যমে ভাগ্যক্রমে আমাদের কাছ থেকে লুকানো থাকে hidden

আপনার যদি প্রচুর পরিমাণে এই অপারেশনগুলির প্রয়োজন হয় তবে ব্যবহার year_monthকরা খুব বেশি ব্যবহারিক নয়। আপনি যদি তা না করেন তবে এটি আপনার উদ্দেশ্যকে পরিষ্কার করার একটি খুব স্পষ্ট উপায় way


বিকল্প হিসাবে, আপনি কোনও year_monthপ্রকারটি সংজ্ঞায়িত করতে এবং একটি অপারেটর year_month+ interval, এবং অন্যটিও সংজ্ঞায়িত করতে পারেন year_month- year_monthএবং গণনাগুলি লুকান। অনুশীলনের প্রয়োজন অনুভব করার জন্য আমি আসলে এত ভারী ব্যবহার কখনই করি নি। এ date- dateআসলে আপনাকে অনুরূপ কিছু গোপন করছে।


1
আমি এটি করার আরও একটি উপায় লিখেছি =) এটি উপভোগ করুন।
ইভান ক্যারল

আমি কীভাবে পাশাপাশি উপকারিতা এবং কন্দের প্রশংসা করি।
ফুনেহে

4

জোয়ানোলোর পদ্ধতির বিকল্প হিসাবে =) (দুঃখিত আমি ব্যস্ত ছিলাম তবে এটি লিখতে চেয়েছিলাম)

বিট জয়

আমরা একই জিনিস করতে যাচ্ছি, কিন্তু বিট সহ। এক int4পোস্টগ্রি, একটি স্বাক্ষরিত পূর্ণসংখ্যা -2147483648 থেকে +2147483647 পর্যন্ত

আমাদের কাঠামোর একটি ওভারভিউ এখানে।

               bit                
----------------------------------
 YYYYYYYYYYYYYYYYYYYYYYYYYYYYMMMM

সংরক্ষণের মাস

  • এক মাস 12 অপশন প্রয়োজন pow(2,4)হয় 4 টি বিট
  • বাকীটি আমরা বছরের জন্য 32-2 = 28 বিট উত্সর্গ করি ।

মাসগুলি কোথায় সংরক্ষণ করা হয় তার বিট মানচিত্র এখানে।

               bit                
----------------------------------
 00000000000000000000000000001111

মাস, 1-জানুয়ারি - 12 ডিসেম্বর

               bit                
----------------------------------
 00000000000000000000000000000001
               bit                
----------------------------------
 00000000000000000000000000001100

বছর। বাকি 28 বিট আমাদের বছরের তথ্য সংরক্ষণের অনুমতি দেয়

SELECT (pow(2,28)-1)::int;
   int4    
-----------
 268435455
(1 row)

এই মুহুর্তে আমাদের সিদ্ধান্ত নেওয়া দরকার যে আমরা এটি কীভাবে করতে চাই। আমাদের উদ্দেশ্যগুলির জন্য, আমরা একটি স্ট্যাটিক অফসেট ব্যবহার করতে পারি, যদি আমাদের কেবল 5000 খ্রিস্টাব্দ প্রয়োজন হয় তবে আমরা ফিরে যেতে পারতাম 268,430,455 BCযা পুরোপুরি মেসোজাইক এবং পুরোপুরি কার্যকর হওয়ার সমস্ত কিছুই জুড়ে ।

SELECT (pow(2,28)-1)::int4::bit(32) << 4;
               year               
----------------------------------
 11111111111111111111111111110000

এবং, এখন আমাদের ধরণের অধ্যয়নগুলি রয়েছে, এটি 2,700 বছরের মধ্যে শেষ হতে চলেছে।

সুতরাং আসুন কিছু ফাংশন তৈরীর উপর কাজ করা যাক।

CREATE DOMAIN year_month AS int4;

CREATE OR REPLACE FUNCTION to_year_month (cstring text)
RETURNS year_month
AS $$
  SELECT (
    ( ((date[1]::int4 - 5000) * -1)::bit(32) << 4 )
    | date[2]::int4::bit(32)
  )::year_month
  FROM regexp_split_to_array(cstring,'-(?=\d{1,2}$)')
    AS t(date)
$$
LANGUAGE sql
IMMUTABLE;

CREATE OR REPLACE FUNCTION year_month_to_text (ym year_month)
RETURNS text
AS $$
  SELECT ((ym::bit(32) >>4)::int4 * -1 + 5000)::text ||
  '-' ||
  (ym::bit(32) <<28 >>28)::int4::text
$$ LANGUAGE sql
IMMUTABLE;

একটি দ্রুত পরীক্ষা এই কাজ করে ..

SELECT year_month_to_text( to_year_month('2014-12') );
SELECT year_month_to_text( to_year_month('-5000-10') );
SELECT year_month_to_text( to_year_month('-8000-10') );
SELECT year_month_to_text( to_year_month('-84398-10') );

এখন আমাদের ফাংশন রয়েছে যা আমরা আমাদের বাইনারি ধরণের উপর ব্যবহার করতে পারি ..

আমরা স্বাক্ষরিত অংশ থেকে আরও একটি বিট কেটে ফেলতে পারতাম, বছরটিকে ইতিবাচক হিসাবে সংরক্ষণ করি এবং তারপরে স্বাক্ষরিত স্বাক্ষর হিসাবে প্রাকৃতিকভাবে সাজিয়ে ফেলতাম। স্টোরেজ স্পেসের তুলনায় যদি গতি উচ্চতর অগ্রাধিকার হত, তবে আমরা যে পথে নামতাম সেই পথটিই এটিই হত। তবে আপাতত আমাদের একটি তারিখ রয়েছে যা মেসোজাইকের সাথে কাজ করে।

আমি পরে এটি আপডেট করতে পারে, শুধু মজা করার জন্য।


সীমাগুলি এখনও সম্ভব নয়, আমি এটি পরে দেখব।
ইভান ক্যারল

আমি মনে করি "বিটটিতে অনুকূলকরণ করা" যখন আপনি "নিম্ন স্তরের সি" তে সমস্ত ফাংশনগুলিও করতে চান তখন সমস্ত ধারণা তৈরি হবে। আপনি ডাউন টু দ্য লাস্ট বিট এবং ডাউন টু দ্য লাস্ট ন্যানোসেকেন্ড ;-) যাইহোক, আনন্দিত! (আমি এখনও
বিসিডির
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.