টেবিল থেকে পরপর "এন" সন্ধান করুন Find


16

এই জাতীয় সংখ্যার সাথে আমার কিছু টেবিল রয়েছে (স্ট্যাটাসটি নিখরচায় বা অ্যাসাইনযুক্ত)

id_set নম্বর স্থিতি         
-----------------------
1 000001 নির্ধারিত
1 000002 বিনামূল্যে
1 000003 নিযুক্ত
1 000004 বিনামূল্যে
1 000005 বিনামূল্যে
1 000006 নির্ধারিত
1 000007 নির্ধারিত
1 000008 বিনামূল্যে
1 000009 বিনামূল্যে
1 000010 বিনামূল্যে
1 000011 নিযুক্ত
1 000012 নির্ধারিত
1 000013 নিযুক্ত
1 000014 বিনামূল্যে
1 000015 নির্ধারিত ED

এবং আমার পরপর "n" সন্ধান করা দরকার, সুতরাং এন = 3 এর জন্য, কোয়েরিটি ফিরে আসবে

1 000008 বিনামূল্যে
1 000009 বিনামূল্যে
1 000010 বিনামূল্যে

এটি প্রতিটি আইডি_সেটের প্রথম সম্ভাব্য গোষ্ঠীটি ফিরে আসবে (বাস্তবে, এটি প্রতিটি ক্যোয়ারী আইডি_সেটের জন্যই কার্যকর করা হবে)

আমি উইন্ডো ফাংশনগুলি যাচাই করছিলাম, কিছু প্রশ্নের মতো চেষ্টা করেছি COUNT(id_number) OVER (PARTITION BY id_set ROWS UNBOUNDED PRECEDING), তবে আমি যা পেয়েছি তা সবই :) আমি যুক্তি নিয়ে ভাবতে পারিনি, পোস্টগ্রিসে কীভাবে এটি করা যায়।

আমি উইন্ডো ফাংশন ব্যবহার করে ভার্চুয়াল কলাম তৈরি করার কথা ভাবছিলাম যেখানে প্রতিটি সংখ্যার জন্য পূর্ববর্তী সারিগুলি গণনা করা হয় যেখানে স্থিতি = 'বিনামূল্যে', ​​তারপরে প্রথম সংখ্যাটি নির্বাচন করুন, যেখানে গণনাটি আমার "এন" সংখ্যার সমান।

অথবা স্থিতি অনুসারে গোষ্ঠী সংখ্যাগুলি হতে পারে তবে কেবলমাত্র একটি অ্যাসাইনগড থেকে অন্য এসএসআইএনএডে এবং কেবলমাত্র কমপক্ষে "এন" সংখ্যাযুক্ত গ্রুপ নির্বাচন করুন

সম্পাদনা

আমি এই ক্যোয়ারীটি পেয়েছি (এবং এটি কিছুটা পরিবর্তন করেছি)

WITH q AS
(
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY id_set, status ORDER BY number) AS rnd,
         ROW_NUMBER() OVER (PARTITION BY id_set ORDER BY number) AS rn
  FROM numbers
)
SELECT id_set,
       MIN(number) AS first_number,
       MAX(number) AS last_number,
       status,
       COUNT(number) AS numbers_count
FROM q
GROUP BY id_set,
         rnd - rn,
         status
ORDER BY
     first_number

যা নিখরচায় / নিযুক্ত সংখ্যার গ্রুপ তৈরি করে তবে আমি শর্তটি পূরণ করে এমন একমাত্র প্রথম গ্রুপের সমস্ত নম্বর পেতে চাই

এসকিউএল ফিডল

উত্তর:


16

এটি একটি সমস্যা। ধরে নিচ্ছি একই id_setসেটে কোনও ফাঁক বা নকল নেই :

WITH partitioned AS (
  SELECT
    *,
    number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp
  FROM atable
  WHERE status = 'FREE'
),
counted AS (
  SELECT
    *,
    COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt
  FROM partitioned
)
SELECT
  id_set,
  number
FROM counted
WHERE cnt >= 3
;

এই কোয়েরির জন্য এখানে একটি এসকিউএল ফিডল ডেমো * লিঙ্ক: http://sqlfiddle.com/#!1/a2633/1

হালনাগাদ

কেবল একটি সেট ফেরত দিতে, আপনি আরও এক রাউন্ড র‌্যাঙ্কে যুক্ত করতে পারেন:

WITH partitioned AS (
  SELECT
    *,
    number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp
  FROM atable
  WHERE status = 'FREE'
),
counted AS (
  SELECT
    *,
    COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt
  FROM partitioned
),
ranked AS (
  SELECT
    *,
    RANK() OVER (ORDER BY id_set, grp) AS rnk
  FROM counted
  WHERE cnt >= 3
)
SELECT
  id_set,
  number
FROM ranked
WHERE rnk = 1
;

এখানে এটির জন্য একটি ডেমোও রয়েছে: http://sqlfiddle.com/#!1/a2633/2

আপনি কি কখনো এক সেট করতে দরকার হয় তাহলে প্রতিid_set পরিবর্তন RANK()ভালো কল:

RANK() OVER (PARTITION BY id_set ORDER BY grp) AS rnk

অতিরিক্তভাবে, আপনি কোয়েরিটিকে সবচেয়ে ছোট ম্যাচিং সেটটি (যেমন প্রথমটি উপস্থিত থাকলে অবশ্যই টানা তিনটি সংখ্যার প্রথম সেটটি ফেরত দেওয়ার চেষ্টা করুন, অন্যথায় চার, পাঁচটি ইত্যাদি) এর মতো করে দিতে পারেন:

RANK() OVER (ORDER BY cnt, id_set, grp) AS rnk

বা এটির মতো (প্রতি এক id_set):

RANK() OVER (PARTITION BY id_set ORDER BY cnt, grp) AS rnk

* এই উত্তরের সাথে যুক্ত এসকিউএল ফিডল ডেমোগুলি 9.1.1 উদাহরণটি 9.2.1 হিসাবে ব্যবহার করেছে যেহেতু এই মুহুর্তে কাজ করছে বলে মনে হচ্ছে না।


আপনাকে অনেক ধন্যবাদ, এটি দেখতে সুন্দর লাগছে, তবে এটি পরিবর্তন করা সম্ভব তাই কেবলমাত্র প্রথম সংখ্যার দলটিই ফিরে আসবে? আমি যদি এটি সিএনটি> = 2 এ পরিবর্তন করি তবে আমি 5 নম্বর পেয়েছি (2 গোষ্ঠী = 2 + 3 সংখ্যা)
বুবিক

@ বুবিক: আপনি কি প্রতি এক id_setবা মাত্র একটি চান? যদি আপনার প্রশ্নটি প্রথম থেকেই এর অংশ হিসাবে বোঝানো হত তবে আপডেট করুন। (যাতে অন্যরা সম্পূর্ণ প্রয়োজনীয়তা দেখতে এবং তাদের প্রস্তাবনাগুলি সরবরাহ করতে বা তাদের উত্তরগুলি আপডেট করতে পারে))
অ্যান্ড্রি এম

আমি আমার প্রশ্নটি সম্পাদন করেছি (প্রত্যাবর্তনের পরে), এটি কেবলমাত্র একটি আইডি_সেটের জন্য কার্যকর করা হবে, সুতরাং কেবল প্রথম সম্ভাব্য গোষ্ঠীটি পাওয়া গেছে
বুবিক

10

একটি সহজ এবং দ্রুত বৈকল্পিক:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • সংখ্যার একটি ফাঁকবিহীন ক্রম প্রয়োজন number(প্রশ্নটিতে সরবরাহ করা হয়েছে)।

  • মধ্যে সম্ভাব্য মান যে কোন সংখ্যার জন্য কাজ করে statusব্যতীত 'FREE', এমনকি সঙ্গে NULL

  • প্রধান বৈশিষ্ট্যটি বিয়োগ করা row_number()number অ-যোগ্যতার সারিগুলি দেওয়ার পরে করা । ধারাবাহিক সংখ্যা একই হিসাবে শেষ হয় grp- এবং আরোহী ক্রমgrp হিসাবে গ্যারান্টিযুক্ত হয় ।

  • তারপর তুমি পারো GROUP BY grp সদস্যদের গণনা । যেহেতু আপনি প্রথম উপস্থিতিটি চান বলে মনে হয় , ORDER BY grp LIMIT 1এবং আপনি সূচনা অবস্থান এবং ক্রমটির দৈর্ঘ্য পাবেন (>> এন হতে পারে )।

সারি সেট

সংখ্যার আসল সেট পেতে, অন্য সময় টেবিলটি সন্ধান করবেন না। এর সাথে অনেক সস্তা generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

আপনি যদি আপনার উদাহরণের মানগুলিতে প্রদর্শিত আপনার মতো শীর্ষস্থানীয় শূন্যগুলির সাথে একটি স্ট্রিং চান তবে ব্যবহার করুন to_char() তবে FM(ফিল্ড মোড) সংশোধকটি ব্যবহার করুন:

SELECT to_char(generate_series(8, 11), 'FM000000')

বর্ধিত পরীক্ষার কেস এবং উভয় প্রশ্নের সাথে এসকিউএল ফিডল

ঘনিষ্ঠভাবে সম্পর্কিত উত্তর:


8

এটি করার জন্য এটি মোটামুটি জেনেরিক উপায়।

মনে রাখবেন এটি আপনার numberকলামটি নিয়মিত থাকার উপর নির্ভর করে । যদি এটি উইন্ডো ফাংশন না হয় এবং / অথবা সিটিই টাইপ-সলিউশন সম্ভবত প্রয়োজন হবে:

SELECT 
    number
FROM
    mytable m
CROSS JOIN
   (SELECT 3 AS consec) x
WHERE 
    EXISTS
       (SELECT 1 
        FROM mytable
        WHERE number = m.number - x.consec + 1
        AND status = 'FREE')
    AND NOT EXISTS
       (SELECT 1 
        FROM mytable
        WHERE number BETWEEN m.number - x.consec + 1 AND m.number
        AND status = 'ASSIGNED')

ঘোষণাপত্র পোস্টগ্র্রেসে এর মতো কাজ করবে না।
a_horse_with_no_name

@ এ_হর্স_বিহীন_নাম_নাম দয়া করে নিখরচায় এটি নির্ধারণ করুন :)
জেএনকে

কোন উইন্ডো ফাংশন, খুব সুন্দর! যদিও আমি মনে করি এটি হওয়া উচিত M.number-consec+1(যেমন 10 এর জন্য এটি হওয়া দরকার 10-3+1=8) need
অ্যান্ড্রি এম

@ অ্যান্ড্রিএম ওয়েল এটি "সুন্দর" নয় কারণ এটি numberক্ষেত্রের অনুক্রমিক মানের উপর নির্ভর করে । গণিতে শুভ কল আমি এটি সংশোধন করব।
জেএনকে

2
পোস্টগ্রিসের সিনট্যাক্স ঠিক করার জন্য আমি স্বাধীনতা নিয়েছি। প্রথমটি EXISTSসরল করা যায়। যেহেতু শুধুমাত্র আমাদের নিশ্চিত হওয়া দরকার যে কোনও পূর্বের সারি রয়েছে, তাই আমরা এটিকে ফেলে দিতে পারি AND status = 'FREE'। এবং আমি ভবিষ্যতে যুক্ত বিকল্পগুলির বিরুদ্ধে কঠোর EXISTSকরার status <> 'FREE'জন্য ২ য় শর্তটি পরিবর্তন করব ।
এরউইন ব্র্যান্ডস্টেটার

5

এটি 3 সংখ্যার মধ্যে প্রথমটি ফিরে আসবে। এটির প্রয়োজন হয় না যে মানগুলি numberক্রমাগত হয়। এসকিউএল-ফিডল পরীক্ষিত :

WITH cte3 AS
( SELECT
    *,
    COUNT(CASE WHEN status = 'FREE' THEN 1 END) 
        OVER (PARTITION BY id_set ORDER BY number
              ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
      AS cnt
  FROM atable
)
SELECT
  id_set, number
FROM cte3
WHERE cnt = 3 ;

এবং এটি সমস্ত সংখ্যা (যেখানে 3 বা ততোধিক 'FREE'পজিশন রয়েছে) প্রদর্শিত হবে:

WITH cte3 AS
( SELECT
    *,
    COUNT(CASE WHEN status = 'FREE' THEN 1 END) 
        OVER (PARTITION BY id_set ORDER BY number
              ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
      AS cnt
  FROM atable
)
, cte4 AS
( SELECT
    *, 
    MAX(cnt) 
        OVER (PARTITION BY id_set ORDER BY number
              ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
      AS maxcnt
  FROM cte3
)
SELECT
  id_set, number
FROM cte4
WHERE maxcnt >= 3 ;

0
select r1.number from some_table r1, 
some_table r2,
some_table r3,
some_table r4 
where r3.number <= r2.number 
and r3.number >= r1.number 
and r3.status = 'FREE' 
and r2.number = r1.number + 4 
and r4.number <= r2.number 
and r4.number >= r1.number 
and r4.status = 'ASSIGNED'
group by r1.number, r2.number having count(r3.number) = 5 and count(r4.number) = 0 order by r1.number asc limit 1 ;

এই ক্ষেত্রে টানা 5 নম্বর - সুতরাং পার্থক্য 4 বা অন্য কথায় count(r3.number) = nএবং হতে হবে r2.number = r1.number + n - 1

যোগদান:

select r1.number 
from some_table r1 join 
 some_table r2 on (r2.number = r1.number + :n -1) join
 some_table r3 on (r3.number <= r2.number and r3.number >= r1.number) join
 some_table r4 on (r4.number <= r2.number and r4.number >= r1.number)
where  
 r3.status = 'FREE' and
 r4.status = 'ASSIGNED'
group by r1.number, r2.number having count(r3.number) = :n and count(r4.number) = 0 order by r1.number asc limit 1 ;

আপনি কী ভাবেন 4-উপায় কার্তেসিয়ান পণ্য এটি করার একটি দক্ষ উপায়?
জেএনকে

বিকল্প হিসাবে আপনি আধুনিক JOINসিনট্যাক্স দিয়ে এটি লিখতে পারেন ?
জেএনকে

ওয়েল আমি উইন্ডো ফাংশনগুলির উপর নির্ভর করতে চাইনি এবং এমন একটি সমাধান দিয়েছিলাম যা কোনও স্কাইল-ডিবিতে কাজ করবে।
ইউনোকটিয়াম

-1
CREATE TABLE #ConsecFreeNums
(
     id_set BIGINT
    ,number VARCHAR(10)
    ,status VARCHAR(10)
)

CREATE TABLE #ConsecFreeNumsResult
(
     Seq    INT
    ,id_set BIGINT
    ,number VARCHAR(10)
    ,status VARCHAR(10)
)

INSERT #ConsecFreeNums
SELECT 1, '000002', 'FREE' UNION
SELECT 1, '000003', 'ASSIGNED' UNION
SELECT 1, '000004', 'FREE' UNION
SELECT 1, '000005', 'FREE' UNION
SELECT 1, '000006', 'ASSIGNED' UNION
SELECT 1, '000007', 'ASSIGNED' UNION
SELECT 1, '000008', 'FREE' UNION
SELECT 1, '000009', 'FREE' UNION
SELECT 1, '000010', 'FREE' UNION
SELECT 1, '000011', 'ASSIGNED' UNION
SELECT 1, '000012', 'ASSIGNED' UNION
SELECT 1, '000013', 'ASSIGNED' UNION
SELECT 1, '000014', 'FREE' UNION
SELECT 1, '000015', 'ASSIGNED'

DECLARE @id_set AS BIGINT, @number VARCHAR(10), @status VARCHAR(10), @number_count INT, @number_count_check INT

DECLARE ConsecFreeNumsCursor CURSOR FAST_FORWARD FOR
SELECT
       id_set
      ,number
      ,status
 FROM
      #ConsecFreeNums
WHERE id_set = 1
ORDER BY number

OPEN ConsecFreeNumsCursor

FETCH NEXT FROM ConsecFreeNumsCursor INTO @id_set, @number, @status

SET @number_count_check = 3
SET @number_count = 0

WHILE @@FETCH_STATUS = 0
BEGIN
    IF @status = 'ASSIGNED'
    BEGIN
        IF @number_count = @number_count_check
        BEGIN
            SELECT 'Results'
            SELECT * FROM #ConsecFreeNumsResult ORDER BY number
            BREAK
        END
        SET @number_count = 0
        TRUNCATE TABLE #ConsecFreeNumsResult
    END
    ELSE
    BEGIN
        SET @number_count = @number_count + 1
        INSERT #ConsecFreeNumsResult SELECT @number_count, @id_set, @number, @status
    END
    FETCH NEXT FROM ConsecFreeNumsCursor INTO @id_set, @number, @status
END

CLOSE ConsecFreeNumsCursor
DEALLOCATE ConsecFreeNumsCursor

DROP TABLE #ConsecFreeNums
DROP TABLE #ConsecFreeNumsResult

আমি আরও ভাল পারফরম্যান্সের জন্য কার্সার ব্যবহার করছি - নির্বাচন করে কি প্রচুর পরিমাণে সারি ফিরতে হবে
রবি রামস্বামী

আমি কোডটি হাইলাইট করে এবং { }সম্পাদকের বোতাম টিপে আপনার উত্তরটির পুনরায় ফর্ম্যাট করেছি । উপভোগ করুন!
jcolebrand

আপনি নিজের উত্তরটি সম্পাদনা করতে এবং কার্সারটি আরও ভাল পারফরম্যান্স কেন সরবরাহ করেন বলে আপনি মনে করতে পারেন।
jcolebrand

কার্সার একটি ক্রমিক প্রক্রিয়া। এটি প্রায় একবারে ফ্ল্যাট ফাইলের একটি রেকর্ড পড়ার মতো। একটি পরিস্থিতিতে আমি একক কার্সার দিয়ে এমইএম টেম্প টেবিলটি প্রতিস্থাপন করেছি। এটি প্রক্রিয়াজাতকরণের সময়টি 26 ঘন্টা থেকে 6 ঘন্টা এনেছে। ফলাফলসেটটি লুপ করতে আমাকে নেসটেড WHILE ব্যবহার করতে হয়েছিল।
রবি রামস্বামী

আপনি কি কখনও নিজের অনুমানগুলি পরীক্ষা করার চেষ্টা করেছেন? আপনি অবাক হতে পারেন। কোণার কেস বাদে প্লেইন এসকিউএল দ্রুততম।
এরউইন ব্র্যান্ডস্টেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.