আমি text
প্রাসঙ্গিক কলামগুলির জন্য ডেটা টাইপ ধরে নিচ্ছি ।
CREATE TABLE prefix (code text, name text, price int);
CREATE TABLE num (number text, time int);
"সরল" সমাধান
SELECT DISTINCT ON (1)
n.number, p.code
FROM num n
JOIN prefix p ON right(n.number, -1) LIKE (p.code || '%')
ORDER BY n.number, p.code DESC;
মূল উপাদান:
DISTINCT ON
এসকিউএল স্ট্যান্ডার্ডের পোস্টগ্রিস এক্সটেনশন DISTINCT
। এসও সম্পর্কিত এই সম্পর্কিত উত্তরের ব্যবহৃত ক্যোয়ারী কৌশলটির বিশদ বিবরণ খুঁজুন ।
ORDER BY p.code DESC
দীর্ঘতম ম্যাচটি বাছাই করে, কারণ এর '1234'
পরে বাছাই '123'
(আরোহী ক্রমে)।
সাধারণ এসকিউএল ফিডল ।
সূচি ব্যতীত, ক্যোয়ারীটি খুব দীর্ঘ সময়ের জন্য চলবে (এটি শেষ হওয়ার অপেক্ষায় নেই)। এটি দ্রুত করার জন্য আপনার সূচী সমর্থন প্রয়োজন। অতিরিক্ত মডিউল দ্বারা সরবরাহিত আপনার ট্রিগার সূচকগুলি pg_trgm
ভাল প্রার্থী a আপনাকে জিআইএন এবং জিএসটি সূচকের মধ্যে নির্বাচন করতে হবে। সংখ্যার প্রথম অক্ষরটি কেবল শব্দ এবং এটি সূচক থেকে বাদ দেওয়া যেতে পারে, এটি এটিকে ছাড়াও কার্যকরী সূচক হিসাবে তৈরি করে।
আমার পরীক্ষাগুলিতে, একটি কার্যকরী ট্রিগার জিআইএন সূচক একটি ট্রিগার জিআইএসটি সূচক (প্রত্যাশারূপে) এর উপরে দৌড় জিতেছে:
CREATE INDEX num_trgm_gin_idx ON num USING gin (right(number, -1) gin_trgm_ops);
উন্নত dbfiddle এখানে ।
সমস্ত পরীক্ষার ফলাফল হ'ল স্থানীয় পোস্টগ্রিস 9.1 পরীক্ষা ইনস্টলেশন থেকে হ্রাস সেটআপ সহ: 17 কে সংখ্যা এবং 2 কে কোড:
- মোট রানটাইম: 1719.552 এমএস (ট্রিগ্রাম জিএসটি)
- মোট রানটাইম: 912.329 এমএস ( ট্রিগার জিএন )
এখনও অনেক দ্রুত
সাথে ব্যর্থ প্রচেষ্টা text_pattern_ops
একবার যদি আমরা বিভ্রান্তিকর প্রথম শব্দের চরিত্রটিকে উপেক্ষা করি, তবে এটি নীচে নেমে আসে বাম অ্যাঙ্কার্ড প্যাটার্ন ম্যাচটি। সুতরাং আমি অপারেটর ক্লাসেরtext_pattern_ops
সাথে একটি কার্যকরী বি-ট্রি সূচক চেষ্টা করেছি (কলামের ধরণ ধরে text
)।
CREATE INDEX num_text_pattern_idx ON num(right(number, -1) text_pattern_ops);
এটি একটি একক অনুসন্ধান শব্দটির সাথে সরাসরি অনুসন্ধানের জন্য দুর্দান্তভাবে কাজ করে এবং ত্রিগ্রাম সূচকে তুলনায় খারাপ দেখায়:
SELECT * FROM num WHERE right(number, -1) LIKE '2345%'
- মোট রানটাইম: ৩.৮১ ms এমএস (ট্রিজিএম_জিন_আইডিএক্স)
- মোট রানটাইম: 0.147 এমএস (টেক্সট_প্যাটার্ন_আইডিএক্স)
তবে ক্যোয়ারী পরিকল্পনাকারী দুটি সারণিতে যোগদানের জন্য এই সূচকটি বিবেচনা করবেন না। আমি এই সীমাবদ্ধতা আগেও দেখেছি। এর জন্য আমার এখনও কোনও অর্থপূর্ণ ব্যাখ্যা নেই।
আংশিক / ক্রিয়ামূলক বি-ট্রি সূচক
আংশিক সূচকগুলির সাথে আংশিক স্ট্রিংগুলিতে সমতা পরীক্ষার বিকল্প এটি। এটি একটি ব্যবহার করা যেতে পারে JOIN
।
যেহেতু সাধারণত আমাদের কেবল different lengths
উপসর্গের জন্য সীমিত সংখ্যক থাকে তাই আমরা এখানে আংশিক সূচকগুলির সাথে অনুরূপ সমাধান তৈরি করতে পারি ।
বলুন, আমাদের 1 থেকে 5 টি অক্ষর পর্যন্ত উপসর্গ রয়েছে । বেশ কয়েকটি আংশিক ক্রিয়ামূলক সূচক তৈরি করুন, প্রতিটি পৃথক উপসর্গ দৈর্ঘ্যের জন্য একটি:
CREATE INDEX prefix_code_idx5 ON prefix(code) WHERE length(code) = 5;
CREATE INDEX prefix_code_idx4 ON prefix(code) WHERE length(code) = 4;
CREATE INDEX prefix_code_idx3 ON prefix(code) WHERE length(code) = 3;
CREATE INDEX prefix_code_idx2 ON prefix(code) WHERE length(code) = 2;
CREATE INDEX prefix_code_idx1 ON prefix(code) WHERE length(code) = 1;
যেহেতু এগুলি আংশিক সূচক, তাই এগুলির সবগুলিই একক সম্পূর্ণ সূচকের চেয়ে সবেমাত্র বড়।
সংখ্যার জন্য মিলে যাওয়া সূচকগুলি যুক্ত করুন (শীর্ষস্থানীয় শব্দের চরিত্রটি অ্যাকাউন্টে নেওয়া):
CREATE INDEX num_number_idx5 ON num(substring(number, 2, 5)) WHERE length(number) >= 6;
CREATE INDEX num_number_idx4 ON num(substring(number, 2, 4)) WHERE length(number) >= 5;
CREATE INDEX num_number_idx3 ON num(substring(number, 2, 3)) WHERE length(number) >= 4;
CREATE INDEX num_number_idx2 ON num(substring(number, 2, 2)) WHERE length(number) >= 3;
CREATE INDEX num_number_idx1 ON num(substring(number, 2, 1)) WHERE length(number) >= 2;
এই সূচীগুলি কেবল প্রতিটি স্ট্রিং ধারণ করে এবং আংশিক হলেও প্রতিটি সারণির বেশিরভাগ বা সমস্তটি কভার করে। সুতরাং তারা একক মোট সূচকের চেয়ে একসাথে অনেক বড় - দীর্ঘ সংখ্যা বাদে। এবং তারা লেখার ক্রিয়াকলাপের জন্য আরও কাজ চাপিয়ে দেয়। আশ্চর্যজনক গতির জন্য এটিই ব্যয় ।
যদি সেই খরচটি আপনার জন্য খুব বেশি হয় (লেখার পারফরম্যান্স গুরুত্বপূর্ণ / অনেকগুলি রাইটিং অপারেশন / ডিস্ক স্পেস কোনও সমস্যা) তবে আপনি এই সূচিগুলি এড়িয়ে যেতে পারেন। বাকিগুলি এখনও দ্রুততর, যদি এটি তত দ্রুত না হয় তবে ...
যদি সংখ্যাগুলি কখনই সংক্ষিপ্ত না হয় তবে n
অক্ষরগুলি, WHERE
কিছু বা সমস্ত থেকে অপ্রয়োজনীয় ধারাগুলি বাদ দিন এবং WHERE
নীচের সমস্ত প্রশ্নের থেকে সংশ্লিষ্ট ধারাটি বাদ দিন drop
পুনরাবৃত্তি সিটিই
এখনও অবধি সমস্ত সেটআপ নিয়ে আমি প্রত্যাবর্তনকারী সিটিই দিয়ে খুব মার্জিত সমাধানের আশা করছিলাম :
WITH RECURSIVE cte AS (
SELECT n.number, p.code, 4 AS len
FROM num n
LEFT JOIN prefix p
ON substring(number, 2, 5) = p.code
AND length(n.number) >= 6 -- incl. noise character
AND length(p.code) = 5
UNION ALL
SELECT c.number, p.code, len - 1
FROM cte c
LEFT JOIN prefix p
ON substring(number, 2, c.len) = p.code
AND length(c.number) >= c.len+1 -- incl. noise character
AND length(p.code) = c.len
WHERE c.len > 0
AND c.code IS NULL
)
SELECT number, code
FROM cte
WHERE code IS NOT NULL;
- মোট রানটাইম: 1045.115 এমএস
যাইহোক, যদিও এই কোয়েরিটি খারাপ নয় - এটি একটি ট্রিগার জিআইএন সূচক সহ সাধারণ সংস্করণ হিসাবে প্রায় ভাল সঞ্চালন করে - এটি আমার লক্ষ্যটি কী ছিল তা সরবরাহ করে না। পুনরাবৃত্তির শব্দটি একবারেই পরিকল্পনা করা হয়, সুতরাং এটি সেরা সূচকগুলি ব্যবহার করতে পারে না। কেবল পুনরাবৃত্তিযোগ্য শব্দটিই পারে।
ইউনিয়ন সব
যেহেতু আমরা অল্প সংখ্যক পুনরাবৃত্তি নিয়ে কাজ করছি আমরা কেবল তাদের পুনরাবৃত্তভাবে বানান করতে পারি। এটি তাদের প্রত্যেকের জন্য অনুকূলিত পরিকল্পনার অনুমতি দেয়। (যদিও আমরা ইতিমধ্যে সফল সংখ্যার পুনরাবৃত্তি বর্জনটি হারাচ্ছি, সুতরাং উন্নতির জন্য এখনও কিছু জায়গা রয়েছে, বিশেষত উপসর্গের দীর্ঘ দৈর্ঘ্যের জন্য):
SELECT DISTINCT ON (1) number, code
FROM (
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 5) = p.code
AND length(n.number) >= 6 -- incl. noise character
AND length(p.code) = 5
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 4) = p.code
AND length(n.number) >= 5
AND length(p.code) = 4
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 3) = p.code
AND length(n.number) >= 4
AND length(p.code) = 3
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 2) = p.code
AND length(n.number) >= 3
AND length(p.code) = 2
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 1) = p.code
AND length(n.number) >= 2
AND length(p.code) = 1
) x
ORDER BY number, code DESC;
- মোট রানটাইম: 57.578 এমএস (!!)
একটি যুগান্তকারী, অবশেষে!
এসকিউএল ফাংশন
এটি একটি এসকিউএল ফাংশনে মোড়ানো পুনরাবৃত্ত ব্যবহারের জন্য ক্যোয়ারী প্ল্যানিং ওভারহেডটিকে সরিয়ে দেয়:
CREATE OR REPLACE FUNCTION f_longest_prefix()
RETURNS TABLE (number text, code text) LANGUAGE sql AS
$func$
SELECT DISTINCT ON (1) number, code
FROM (
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 5) = p.code
AND length(n.number) >= 6 -- incl. noise character
AND length(p.code) = 5
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 4) = p.code
AND length(n.number) >= 5
AND length(p.code) = 4
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 3) = p.code
AND length(n.number) >= 4
AND length(p.code) = 3
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 2) = p.code
AND length(n.number) >= 3
AND length(p.code) = 2
UNION ALL
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(number, 2, 1) = p.code
AND length(n.number) >= 2
AND length(p.code) = 1
) x
ORDER BY number, code DESC
$func$;
কল করুন:
SELECT * FROM f_longest_prefix_sql();
- মোট রানটাইম: 17.138 এমএস (!!!)
ডায়নামিক এসকিউএল সহ পিএল / পিজিএসকিউএল ফাংশন
এই plpgsql ফাংশনটি উপরের পুনরাবৃত্ত সিটিইর মতো, তবে গতিশীল এসকিউএল EXECUTE
কোয়েরিটিকে প্রতিটি পুনরাবৃত্তির জন্য পুনরায় পরিকল্পনা করতে বাধ্য করে। এখন এটি সমস্ত তৈরি সূচকগুলি ব্যবহার করে।
অতিরিক্তভাবে এটি কোনও উপসর্গ দৈর্ঘ্যের জন্য কাজ করে । ফাংশনটি ব্যাপ্তির জন্য দুটি পরামিতি নেয়, তবে আমি এটি DEFAULT
মান সহ প্রস্তুত করেছি , সুতরাং এটি সুস্পষ্ট পরামিতি ছাড়াইও কাজ করে:
CREATE OR REPLACE FUNCTION f_longest_prefix2(_min int = 1, _max int = 5)
RETURNS TABLE (number text, code text) LANGUAGE plpgsql AS
$func$
BEGIN
FOR i IN REVERSE _max .. _min LOOP -- longer matches first
RETURN QUERY EXECUTE '
SELECT n.number, p.code
FROM num n
JOIN prefix p
ON substring(n.number, 2, $1) = p.code
AND length(n.number) >= $1+1 -- incl. noise character
AND length(p.code) = $1'
USING i;
END LOOP;
END
$func$;
চূড়ান্ত পদক্ষেপটি সহজেই একটি ফাংশনে আবৃত করা যায় না।
হয় কেবল এটিকে কল করুন:
SELECT DISTINCT ON (1)
number, code
FROM f_longest_prefix_prefix2() x
ORDER BY number, code DESC;
অথবা মোড়ক হিসাবে অন্য একটি এসকিউএল ফাংশন ব্যবহার করুন:
CREATE OR REPLACE FUNCTION f_longest_prefix3(_min int = 1, _max int = 5)
RETURNS TABLE (number text, code text) LANGUAGE sql AS
$func$
SELECT DISTINCT ON (1)
number, code
FROM f_longest_prefix_prefix2($1, $2) x
ORDER BY number, code DESC
$func$;
কল করুন:
SELECT * FROM f_longest_prefix3();
ওভারহেডের প্রয়োজনীয় পরিকল্পনার কারণে কিছুটা ধীর। তবে এসকিউএল এর চেয়ে বেশি বহুমুখী এবং দীর্ঘ উপসর্গের জন্য সংক্ষিপ্ত।
code
প্রথম টেবিলের পরে যদি উপসর্গের মতো হয় তবে সত্যই আমি নিশ্চিত নই । আপনি দয়া করে এটি পরিষ্কার করতে পারেন? এবং উদাহরণস্বরূপ ডেটা এবং কাঙ্ক্ষিত আউটপুট (যাতে আপনার সমস্যাটি অনুসরণ করা সহজতর হয়) এর কিছু সংশোধনও স্বাগত হবে।