সারিগুলি সন্ধান করুন যেখানে পূর্ণসংখ্যার ক্রমটিতে একটি প্রদত্ত অনুচ্ছেদ রয়েছে


9

সমস্যা

দ্রষ্টব্য: আমি গাণিতিক ক্রমগুলি উল্লেখ করি , পোস্টগ্রেএসকিউএল এর সিকোয়েন্সগুলি নয় ।

আমার কাছে পূর্ণসংখ্যার ক্রমিক প্রতিনিধিত্বকারী একটি টেবিল রয়েছে। সংজ্ঞাটি হ'ল:

CREATE TABLE sequences
(
  id serial NOT NULL,
  title character varying(255) NOT NULL,
  date date NOT NULL,
  sequence integer[] NOT NULL,
  CONSTRAINT "PRIM_KEY_SEQUENCES" PRIMARY KEY (id)
);

আমার লক্ষ্য একটি প্রদত্ত অনুচ্ছেদ ব্যবহার করে সারি সন্ধান করা। এটি বলতে গেলে, sequenceক্ষেত্রটি যে সারিগুলিতে একটি অনুক্রম থাকে তাতে প্রদত্ত অনুচ্ছেদ থাকে (আমার ক্ষেত্রে, সিকোয়েন্সটি অর্ডার করা হয়)।

উদাহরণ

ধরুন সারণীতে নিম্নলিখিত তথ্য রয়েছে:

+----+-------+------------+-------------------------------+
| id | title |    date    |           sequence            |
+----+-------+------------+-------------------------------+
|  1 | BG703 | 2004-12-24 | {1,3,17,25,377,424,242,1234}  |
|  2 | BG256 | 2005-05-11 | {5,7,12,742,225,547,2142,223} |
|  3 | BD404 | 2004-10-13 | {3,4,12,5698,526}             |
|  4 | BK956 | 2004-08-17 | {12,4,3,17,25,377,456,25}     |
+----+-------+------------+-------------------------------+

সুতরাং প্রদত্ত অনুচ্ছেদটি যদি হয় তবে {12, 742, 225, 547}আমি সারি 2 সন্ধান করতে চাই।

একইভাবে, প্রদত্ত অনুচ্ছেদটি যদি হয় তবে {3, 17, 25, 377}আমি সারি 1 এবং সারি 4 সন্ধান করতে চাই।

শেষ অবধি, যদি প্রদত্ত অনুচ্ছেদটি হয় {12, 4, 3, 25, 377}, তবে কোনও সারি ফিরে আসে না।

তদন্ত

প্রথমত, আমি সম্পূর্ণরূপে নিশ্চিত নই যে অ্যারের ডেটা টাইপের সাথে সিকোয়েন্সগুলি প্রতিনিধিত্ব করা বুদ্ধিমান। যদিও এটি পরিস্থিতিটিকে উপযুক্ত বলে মনে হয়; আমি আশঙ্কা করি যে এটি আরও জটিল পরিচালনা করে। অন্য টেবিলে সম্পর্কের মডেল ব্যবহার করে, সিকোয়েন্সগুলি আলাদাভাবে উপস্থাপন করা আরও ভাল।

একইভাবে, আমি unnestঅ্যারের ফাংশনটি ব্যবহার করে ক্রমগুলি প্রসারিত করার বিষয়ে ভাবি এবং তারপরে আমার অনুসন্ধানের মানদণ্ডটি যুক্ত করি। তবুও, অনুক্রমের শর্তগুলির সংখ্যাটি পরিবর্তনশীল হওয়ার কারণে আমি এটি দেখতে পারি না do

আমি জানি যে ইনটারে মডিউলটির subarrayক্রিয়াকলাপটি ব্যবহার করে আমার সিকোয়েন্সটি কাটাও সম্ভব তবে এটি আমার অনুসন্ধানের জন্য কীভাবে আমাকে উপকৃত করবে তা আমি দেখতে পাচ্ছি না।

সীমাবদ্ধতাসমূহ

এমনকি যদি এই মুহুর্তে আমার মডেলটি এখনও বিকাশ করা হচ্ছে তবে টেবিলটি 50,000 থেকে 300,000 সারিগুলির মধ্যে অনেকগুলি সিকোয়েন্সের সমন্বয়ে গঠিত। সুতরাং আমি একটি শক্তিশালী পারফরম্যান্স বাধা আছে।

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


যদি তারা উপচে পড়তে পারে তবে এগুলি সংরক্ষণ করার জন্য আপনার টাইপ হিসাবে bigintব্যবহার numericকরা উচিত । এটি অনেক ধীর এবং যদিও আরও বেশি জায়গা নেয়।
ক্রেগ রিঞ্জার

@ ক্রেইগ্রিঞ্জার কেন numericস্ট্রিং ব্যবহার করবেন না ( textউদাহরণস্বরূপ)? আমার ক্রমগুলিতে গাণিতিক ক্রিয়াকলাপ করার দরকার নেই perform
এমএলপো

2
কারণ এটি আরও কমপ্যাক্ট এবং বিভিন্ন দিক থেকে আরও দ্রুত textএবং এটি আপনাকে বোগাস অ-সংখ্যাগত ডেটা সংরক্ষণ করতে বাধা দেয়। নির্ভর করে, আপনি কেবলমাত্র I / O করছেন যদি আপনি পাঠ্য I / O প্রসেসিং হ্রাস করতে পারে।
ক্রেগ রিঞ্জার

@ ক্রেইগ্রিঞ্জার প্রকৃতপক্ষে, প্রকারটি আরও সুসংগত। পারফরম্যান্স সম্পর্কে, আমি যখন আমার অনুসন্ধান করার কোনও উপায় খুঁজে পেয়েছি তখন পরীক্ষা করব।
এমএলপো

2
@ ক্র্যাগরিঞ্জার যদি আদেশের বিষয়টি বিবেচনা না করে তবে এটি কাজ করতে পারে। তবে এখানে, ক্রমগুলি অর্ডার করা হয়। উদাহরণ: SELECT ARRAY[12, 4, 3, 17, 25, 377, 456, 25] @> ARRAY[12, 4, 3, 25, 377];সত্য ফিরে আসবে, কারণ এই অপারেটর দ্বারা অর্ডার বিবেচনা করা হয় না।
এমএলপো

উত্তর:


3

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

এখানে int4 অ্যারেগুলির জন্য একটি উদাহরণ। ( একটি জেনেরিক অ্যারে বৈকল্পিক এবং সংশ্লিষ্ট এসকিউএল স্ক্রিপ্ট )।

Datum
_int_sequence_contained(PG_FUNCTION_ARGS)
{
    return DirectFunctionCall2(_int_contains_sequence,
                               PG_GETARG_DATUM(1),
                               PG_GETARG_DATUM(0));
}

Datum
_int_contains_sequence(PG_FUNCTION_ARGS)
{
    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);
    ArrayType  *b = PG_GETARG_ARRAYTYPE_P(1);
    int         na, nb;
    int32      *pa, *pb;
    int         i, j;

    na = ArrayGetNItems(ARR_NDIM(a), ARR_DIMS(a));
    nb = ArrayGetNItems(ARR_NDIM(b), ARR_DIMS(b));
    pa = (int32 *) ARR_DATA_PTR(a);
    pb = (int32 *) ARR_DATA_PTR(b);

    /* The naive searching algorithm. Replace it with a better one if your arrays are quite large. */
    for (i = 0; i <= na - nb; ++i)
    {
        for (j = 0; j < nb; ++j)
            if (pa[i + j] != pb[j])
                break;

        if (j == nb)
            PG_RETURN_BOOL(true);
    }

    PG_RETURN_BOOL(false);
}
CREATE FUNCTION _int_contains_sequence(_int4, _int4)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT IMMUTABLE;

CREATE FUNCTION _int_sequence_contained(_int4, _int4)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT IMMUTABLE;

CREATE OPERATOR @@> (
  LEFTARG = _int4,
  RIGHTARG = _int4,
  PROCEDURE = _int_contains_sequence,
  COMMUTATOR = '<@@',
  RESTRICT = contsel,
  JOIN = contjoinsel
);

CREATE OPERATOR <@@ (
  LEFTARG = _int4,
  RIGHTARG = _int4,
  PROCEDURE = _int_sequence_contained,
  COMMUTATOR = '@@>',
  RESTRICT = contsel,
  JOIN = contjoinsel
);

এখন আপনি এটির মতো সারিগুলি ফিল্টার করতে পারেন।

SELECT * FROM sequences WHERE sequence @@> '{12, 742, 225, 547}'

এই সমাধানটি কত দ্রুত তা খুঁজে পেতে আমি একটু পরীক্ষা চালিয়েছি।

CREATE TEMPORARY TABLE sequences AS
SELECT array_agg((random() * 10)::int4) AS sequence, g1 AS id
FROM generate_series(1, 100000) g1
  CROSS JOIN generate_series(1, 30) g2
GROUP BY g1;
EXPLAIN ANALYZE SELECT * FROM sequences
WHERE        translate(cast(sequence as text), '{}',',,')
 LIKE '%' || translate(cast('{1,2,3,4}'as text), '{}',',,') || '%'

"Seq Scan on sequences  (cost=0.00..7869.42 rows=28 width=36) (actual time=2.487..334.318 rows=251 loops=1)"
"  Filter: (translate((sequence)::text, '{}'::text, ',,'::text) ~~ '%,1,2,3,4,%'::text)"
"  Rows Removed by Filter: 99749"
"Planning time: 0.104 ms"
"Execution time: 334.365 ms"
EXPLAIN ANALYZE SELECT * FROM sequences WHERE sequence @@> '{1,2,3,4}'

"Seq Scan on sequences  (cost=0.00..5752.01 rows=282 width=36) (actual time=0.178..20.792 rows=251 loops=1)"
"  Filter: (sequence @@> '{1,2,3,4}'::integer[])"
"  Rows Removed by Filter: 99749"
"Planning time: 0.091 ms"
"Execution time: 20.859 ms"

সুতরাং, এটি প্রায় 16 গুণ দ্রুত। যদি এটি পর্যাপ্ত না হয় তবে আপনি জিন বা জিআইএসটি সূচকগুলির জন্য সমর্থন যোগ করতে পারেন, তবে এটি আরও বেশি কঠিন কাজ হবে।


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

আমি নিশ্চিত নই যে বৃহত্তর কোডগুলিকে উত্তরগুলিতে ন্যূনতম এবং যাচাইযোগ্য বলে আটকানো ভাল অভ্যাস কিনা। এই ফাংশনের একটি জেনেরিক অ্যারে সংস্করণ চারগুণ দীর্ঘ এবং বেশ জটিল। আমি এটি দিয়েও পরীক্ষা করেছি numericএবং textউন্নতি অ্যারেগুলির দৈর্ঘ্যের উপর নির্ভর করে 20 থেকে 50 বার পর্যন্ত হয়েছিল।
স্লোনোপটামাস

হ্যাঁ, তবে এটি অবশ্যই জবাবগুলির উত্তরগুলির উত্তর দেয় :-)। এখানে, আমার কাছে মনে হয়েছে যে সীমাবদ্ধতার সাথে সম্মতি জানানো একটি উত্তর আকর্ষণীয় (কারণ এই দিকটি প্রশ্নের একটি অংশ)। তবুও, জেনেরিক সংস্করণ প্রস্তাব করার প্রয়োজন হবে না। স্ট্রিং সহ একটি সংস্করণ বা numeric
এমএলপো

যাইহোক, আমি জেনেরিক অ্যারেগুলির জন্য সংস্করণটি যুক্ত করেছি যেহেতু এটি কোনও পরিবর্তনশীল-দৈর্ঘ্যের ডেটা টাইপের জন্য প্রায় একই রকম। তবে আপনি যদি পারফরম্যান্স সম্পর্কে সত্যই উদ্বিগ্ন হন তবে আপনার যেমন স্থির আকারের ডেটা টাইপ করা উচিত bigint
স্লোনোপটামাস

আমি এটা করতে চাই। সমস্যাটি হ'ল আমার কয়েকটি সিকোয়েন্সগুলি অনেক বেশি দূরে উপচে পড়েছে bigint, সুতরাং দেখে মনে হচ্ছে আমার কোনও বিকল্প নেই। তবে আপনার যদি ধারণা থাকে তবে আমি আগ্রহী :)।
এমএলপো

1

আপনি যখন অ্যারেগুলিতে স্ট্রিংয়ের জন্য কাস্ট করেন এবং কোঁকড়া বন্ধনীগুলি কমা দিয়ে প্রতিস্থাপন করেন তখন আপনি সহজেই অনুচ্ছেদটি সন্ধান করতে পারেন:

translate(cast(sequence as varchar(10000)), '{}',',,')

{1,3,17,25,377,424,242,1234} -> ',1,3,17,25,377,424,242,1234,'

যে অ্যারে আপনি সন্ধান করছেন তার জন্যও এটি করুন এবং একটি শীর্ষস্থানীয় এবং পিছনে যুক্ত করুন %:

'%' || translate(cast(searchedarray as varchar(10000)), '{}',',,') || '%'

{3, 17, 25, 377} -> '%,3,17,25,377,%'

এখন আপনি এটি ব্যবহার করে তুলনা করুন LIKE:

WHERE        translate(cast(sequence      as varchar(10000)), '{}',',,')
 LIKE '%' || translate(cast(searchedarray as varchar(10000)), '{}',',,') || '%'

সম্পাদনা:

ফিডল আবার কাজ করছে।

অ্যারেগুলি যদি প্রতিটি মূল্যকে এক সারিতে স্বাভাবিক করা হয় আপনি সেট ভিত্তিক যুক্তি প্রয়োগ করতে পারেন:

CREATE TABLE sequences
( id int NOT NULL,
  n int not null,
  val numeric not null
);

insert into sequences values(  1, 1,1     );
insert into sequences values(  1, 2,3     );
insert into sequences values(  1, 3,17    );
insert into sequences values(  1, 4,25    );
insert into sequences values(  1, 5,377   );
insert into sequences values(  1, 6,424   );
insert into sequences values(  1, 7,242   );
insert into sequences values(  1, 8,1234  );
insert into sequences values(  2, 1,5     );
insert into sequences values(  2, 2,7     );
insert into sequences values(  2, 3,12    );
insert into sequences values(  2, 4,742   );
insert into sequences values(  2, 5,225   );
insert into sequences values(  2, 6,547   );
insert into sequences values(  2, 7,2142  );
insert into sequences values(  2, 8,223   );
insert into sequences values(  3, 1,3     );
insert into sequences values(  3, 2,4     );
insert into sequences values(  3, 3,12    );
insert into sequences values(  3, 4,5698  );
insert into sequences values(  3, 5,526   );          
insert into sequences values(  4, 1,12    );
insert into sequences values(  4, 2,4     );
insert into sequences values(  4, 3,3     );
insert into sequences values(  4, 4,17    );
insert into sequences values(  4, 5,25    );
insert into sequences values(  4, 6,377   );
insert into sequences values(  4, 7,456   );
insert into sequences values(  4, 8,25    );
insert into sequences values(  5, 1,12    );
insert into sequences values(  5, 2,4     );
insert into sequences values(  5, 3,3     );
insert into sequences values(  5, 4,17    );
insert into sequences values(  5, 5,17    );
insert into sequences values(  5, 6,25    );
insert into sequences values(  5, 7,377   );
insert into sequences values(  5, 8,456   );
insert into sequences values(  5, 9,25    );

nঅবশ্যই অনুক্রমিক, কোনও সদৃশ, কোনও ফাঁক নেই। এখন সাধারণ মানগুলিতে যোগদান করুন এবং ক্রমগুলি ক্রমযুক্ত :-) এটিকে কাজে লাগান

with searched (n,val) as (
  VALUES
   ( 1,3  ),
   ( 2,17 ),
   ( 3,25 ),
   ( 4,377)
)
select seq.id, 
   -- this will return the same result if the values from both tables are in the same order
   -- it's a meaningless dummy, but the same meaningless value for sequential rows 
   seq.n - s.n as dummy,
   seq.val,
   seq.n,
   s.n 
from sequences as seq join searched as s
on seq.val = s.val
order by seq.id, dummy, seq.n;

অবশেষে একই ডামি সহ সারিগুলির সংখ্যা গণনা করুন এবং এটি সঠিক নম্বর কিনা তা পরীক্ষা করুন:

with searched (n,val) as (
  VALUES
   ( 1,3  ),
   ( 2,17 ),
   ( 3,25 ),
   ( 4,377)
)
select distinct seq.id
from sequences as seq join searched as s
on seq.val = s.val
group by 
   seq.id,
   seq.n - s.n
having count(*) = (select count(*) from searched)
;

সিকোয়েন্সগুলিতে একটি সূচক চেষ্টা করুন (ভাল, আইডি, এন)।


আমি পরে এই সমাধান বিবেচনা। তবে আমি বেশ কয়েকটি সমস্যা দেখতে পেয়েছি যা বেশ বিরক্তিকর বলে মনে হচ্ছে: সবার আগে আমি ভয় করি যে এই সমাধানটি খুব অদক্ষ, আমাদের সন্ধানের বিন্যাস তৈরির আগে প্রতিটি সারির প্রতিটি অ্যারে অবশ্যই ফেলে দিতে হবে। একটি TEXTক্ষেত্রের ক্রমগুলি সংরক্ষণ করা বিবেচনা করা সম্ভব ( varcharআমার মতে এটি একটি খারাপ ধারণা, ক্রম সংখ্যা হিসাবে দীর্ঘ হতে পারে, সুতরাং আকারটি বরং অনাকাঙ্ক্ষিত), কাস্ট এড়ানোর জন্য; তবে পারফরম্যান্সগুলি উন্নত করতে সূচিপত্রগুলি ব্যবহার করা এখনও সম্ভব নয় (ততক্ষণে স্ট্রিং ফিল্ডটি ব্যবহার করা ন্যায়বিচারযোগ্য বলে মনে হয় না, উপরে @ ক্রেইগ্রঞ্জার এর মন্তব্য দেখুন)।
এমএলপো

@ এমএলপিও: আপনার পারফরম্যান্সের প্রত্যাশা কী? একটি সূচক ব্যবহার করতে সক্ষম হতে আপনাকে অবশ্যই প্রতি মানকে প্রতিটি সারিতে ক্রমকে স্বাভাবিক করতে হবে, একটি সম্পর্কিত সম্পর্ক বিভাগ প্রয়োগ করতে হবে এবং শেষ পর্যন্ত আদেশটি সঠিক কিনা তা পরীক্ষা করে দেখুন। আপনার উদাহরণে 25দু'বার বিদ্যমান id=4, এটি কি আসলে সম্ভব? অনুসন্ধানের ক্রমের জন্য গড় / সর্বাধিক কত মিল আছে?
dnoeth

একটি ক্রম একই সংখ্যার কয়েকবার থাকতে পারে। উদাহরণ হিসেবে বলা যায় {1, 1, 1, 1, 12, 2, 2, 12, 12, 1, 1, 5, 4}বেশ সম্ভব। ম্যাচের সংখ্যা সম্পর্কে, ব্যবহৃত উপসর্গগুলি সাধারণত ফলাফলের সংখ্যা সীমিত করার জন্য ভাবা হয়। যাইহোক, কিছু সিকোয়েন্সগুলি খুব সাদৃশ্যপূর্ণ এবং আরও ফলাফল পাওয়ার জন্য মাঝে মাঝে ছোট্ট একটি অনুচ্ছেদে ব্যবহার করা আকর্ষণীয় হতে পারে। আমি অনুমান করি যে সংখ্যাগরিষ্ঠ ক্ষেত্রে ম্যাচের সংখ্যা 0 থেকে 100 এর মধ্যে always
এমএলপো

@mlpo: আমি একটি সেট-ভিত্তিক সমাধান জুড়েছে এবং আমি খুব কিছু কর্মক্ষমতা তুলনা :-) আগ্রহী হতে চান
dnoeth

@ টিউবারকিউ: আরও অর্থবহ ফলাফলটি ফিরিয়ে আনার জন্য এটি কেবলমাত্র দ্রুত যোগ ছিল :-) ঠিক আছে, এটি ভয়াবহ, আমি এটি পরিবর্তন করব l
dnoeth
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.