उन पंक्तियों को खोजें जहाँ पूर्णांक अनुक्रम में एक दिए गए परिणाम होते हैं


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सरणी फ़ंक्शन का उपयोग करके अनुक्रमों का विस्तार करने के बारे में सोचता हूं और फिर अपने खोज मापदंड जोड़ता हूं। फिर भी, अनुक्रम में शर्तों की संख्या चर जा रहा है मैं यह नहीं देखता कि यह कैसे करना है।

मुझे पता है कि अंतःप्रेरण मॉड्यूल के subarrayकार्य का उपयोग करके मेरे अनुक्रम में कटौती करना भी संभव है लेकिन मुझे यह नहीं दिखता है कि यह मेरी खोज के लिए मुझे कैसे लाभ पहुंचाता है

प्रतिबन्ध

यहां तक ​​कि अगर इस समय मेरा मॉडल अभी भी विकसित हो रहा है, तो तालिका 50,000 और 300,000 पंक्तियों के बीच कई अनुक्रमों से बना है। इसलिए मेरे पास एक मजबूत प्रदर्शन बाधा है।

मेरे उदाहरण में मैंने अपेक्षाकृत छोटे पूर्णांकों का उपयोग किया है। व्यवहार में, यह संभव है कि ये पूर्णांक बहुत बड़े हो जाएं, अतिप्रवाह तक bigint। ऐसी स्थिति में, मुझे लगता है कि संख्याओं को स्ट्रिंग्स के रूप में संग्रहीत करना सबसे अच्छा है (क्योंकि गणितीय कार्यों के इन अनुक्रमों को निष्पादित करना आवश्यक नहीं है)। हालांकि, इस समाधान के लिए चुनने पर, यह ऊपर वर्णित अंतरंग मॉड्यूल का उपयोग करना असंभव बनाता है ।


यदि वे अतिप्रवाह bigintकर सकते हैं तो आपको numericउन्हें स्टोर करने के प्रकार के रूप में उपयोग करना चाहिए । यह बहुत धीमा है और रास्ते में अधिक जगह लेता है।
क्रेग रिंगर

@CraigRinger numericएक स्ट्रिंग ( textउदाहरण के लिए) का उपयोग क्यों और नहीं ? मुझे अपने अनुक्रमों पर गणितीय कार्य करने की आवश्यकता नहीं है।
एमएलपी

2
क्योंकि यह अधिक कॉम्पैक्ट और कई मायनों में तेजी से है text, और आपको फर्जी गैर-संख्यात्मक डेटा संग्रहीत करने से रोकता है। निर्भर करता है, यदि आप केवल I / O कर रहे हैं, तो आप I / O संसाधन को कम करने के लिए पाठ चाहते हैं।
क्रेग रिंगर

@ क्रेगिंगर वास्तव में, प्रकार अधिक सुसंगत है। प्रदर्शन के बारे में, मैं परीक्षण करूँगा जब मुझे अपनी खोज करने का एक तरीका मिल गया था।
म्प्पो

2
@CraigRinger यह काम कर सकता है अगर ऑर्डर मायने नहीं रखता। लेकिन यहाँ, दृश्यों का आदेश दिया जाता है। उदाहरण: SELECT ARRAY[12, 4, 3, 17, 25, 377, 456, 25] @> ARRAY[12, 4, 3, 25, 377];सही लौटेगा, क्योंकि इस ऑपरेटर द्वारा आदेश नहीं माना जाता है।
म्प्पो

जवाबों:


3

यदि आप dnoeth के उत्तर के लिए महत्वपूर्ण प्रदर्शन सुधारों की तलाश कर रहे हैं , तो एक देशी सी-फ़ंक्शन का उपयोग करने और उपयुक्त ऑपरेटर बनाने पर विचार करें।

यहाँ int4 सरणियों के लिए एक उदाहरण है। ( एक सामान्य सरणी संस्करण और संबंधित SQL स्क्रिप्ट )।

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 गुना तेज है। यदि यह पर्याप्त नहीं है, तो आप GIN या GiST इंडेक्स के लिए समर्थन जोड़ सकते हैं, लेकिन यह बहुत अधिक कठिन कार्य होगा।


दिलचस्प लगता है, हालांकि मैं numericअपने डेटा का प्रतिनिधित्व करने के लिए या तो तार या प्रकार का उपयोग करता हूं क्योंकि वे अतिप्रवाह हो सकते हैं bigint। प्रश्न की बाधाओं का मिलान करने के लिए अपने उत्तर को संपादित करना अच्छी तरह से हो सकता है। वैसे भी, मैं एक तुलनात्मक प्रदर्शन करूंगा जो मैं यहां पोस्ट करूंगा।
18

मुझे यकीन नहीं है कि अगर कोड के बड़े ब्लॉकों को जवाबों में चिपकाने के लिए यह एक अच्छा अभ्यास है क्योंकि उन्हें न्यूनतम और सत्यापन योग्य माना जाता है। इस फ़ंक्शन का एक सामान्य सरणी संस्करण चार गुना लंबा और काफी बोझिल है। मैंने इसका परीक्षण भी किया है numericऔर textइसमें एरियर्स की लंबाई के आधार पर 20 से 50 बार तक सुधार हुआ है।
स्लोनोपोटामस

हाँ, हालाँकि यह आवश्यक है कि प्रश्नों के उत्तर दिए गए :-)। यहाँ, यह मुझे लगता है कि बाधाओं का अनुपालन करने वाला उत्तर दिलचस्प है (क्योंकि यह पहलू प्रश्न का एक हिस्सा है)। बहरहाल, एक सामान्य संस्करण का प्रस्ताव करना आवश्यक नहीं हो सकता है। तार के साथ एक संस्करण या numeric
एमएलपी

वैसे भी, मैंने जेनेरिक सरणियों के लिए संस्करण जोड़ा क्योंकि यह किसी भी चर-लंबाई डेटा प्रकार के लिए लगभग समान होगा। लेकिन अगर आप वास्तव में प्रदर्शन के बारे में चिंतित हैं, तो आपको निश्चित आकार के डेटा प्रकारों के साथ रहना चाहिए bigint
स्लोनोपोटामस

मैं वह करना पसंद करूँगा। समस्या यह है कि मेरे कुछ सीक्वेंस बहुत आगे निकल गए हैं bigint, इसलिए ऐसा लगता है कि मेरे पास कोई विकल्प नहीं है। लेकिन अगर आपके पास एक विचार है, मुझे दिलचस्पी है :)।
mlpo

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मेरी राय में एक बुरा विचार है, अनुक्रम लंबे हो सकते हैं, संख्या के रूप में, इसलिए आकार अप्रत्याशित है), कलाकारों से बचने के लिए; लेकिन प्रदर्शन में सुधार करने के लिए अनुक्रमित का उपयोग करना अभी भी संभव नहीं है (इसके अलावा एक स्ट्रिंग फ़ील्ड का उपयोग आवश्यक रूप से विवेकपूर्ण नहीं लगता है, ऊपर @CraigRinger की टिप्पणी देखें)।
एमएलपीओ

@ एमएलपीओ: आपकी प्रदर्शन अपेक्षा क्या है? एक इंडेक्स का उपयोग करने में सक्षम होने के लिए आपको अनुक्रम को प्रति पंक्ति एक पंक्ति में सामान्य करना होगा, एक रिलेशनल डिवीजन लागू करें और अंत में जांच करें कि क्या ऑर्डर सही है। आपके उदाहरण में 25दो बार मौजूद है id=4, क्या यह वास्तव में संभव है? किसी खोजे गए अनुक्रम के लिए औसत / अधिकतम में कितने मैच मौजूद हैं?
dnoeth

एक क्रम में कई बार एक ही संख्या हो सकती है। उदाहरण के लिए {1, 1, 1, 1, 12, 2, 2, 12, 12, 1, 1, 5, 4}काफी संभव है। मैचों की संख्या के बारे में, आमतौर पर उपयोग किए जाने वाले परिणामों की संख्या को सीमित करने के लिए आमतौर पर सोचा जाता है। हालांकि, कुछ अनुक्रम बहुत समान हैं, और कभी-कभी अधिक परिणाम प्राप्त करने के लिए कमतर का उपयोग करना दिलचस्प हो सकता है। मेरा अनुमान है कि अधिकांश मामलों के लिए मिलानों की संख्या 0 और 100 के बीच है। हमेशा इस संभावना के साथ कि कभी-कभी अनुवर्ती क्रम बहुत सारे दृश्यों के साथ मेल खाता है जब यह छोटा या बहुत सामान्य होता है।
म्प्पो

@mlpo: मैंने एक सेट-आधारित समाधान जोड़ा और मुझे कुछ प्रदर्शन तुलना में बहुत दिलचस्पी होगी :-)
dnoeth

@ypercube: यह एक और अधिक सार्थक परिणाम :-) ठीक है वापस जाने के लिए बस एक त्वरित ऐड था, यह भयानक है, मैं it.l बदल देंगे
dnoeth
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.