मानक SQL या T-SQL में 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1,… श्रृंखला कैसे उत्पन्न करें?


11

दो नंबरों को देखते हुए nऔर mमैं फॉर्म की एक श्रृंखला तैयार करना चाहता हूं

1, 2, ..., (n-1), n, n, (n-1), ... 2, 1

और इसे mबार-बार दोहराएं ।

उदाहरण के लिए, मैं n = 3और m = 4, मैं निम्नलिखित 24 संख्याओं का एक क्रम चाहता हूं:

1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
----------------  ----------------  ----------------  ----------------

मुझे पता है कि दो तरीकों से PostgreSQL में इस परिणाम को कैसे प्राप्त किया जाए:

निम्न क्वेरी का उपयोग करना, जो generate_seriesफ़ंक्शन का उपयोग करता है , और यह सुनिश्चित करने के लिए कुछ तरकीबें कि आदेश सही है:

WITH parameters (n, m) AS
(
    VALUES (3, 5)
)
SELECT 
    xi
FROM
(
    SELECT
        i, i AS xi
    FROM
        parameters, generate_series(1, parameters.n) AS x(i)
    UNION ALL
    SELECT
        i + parameters.n, parameters.n + 1 - i AS xi
    FROM
        parameters, generate_series(1, parameters.n) AS x(i)
) AS s0 
CROSS JOIN 
    generate_series (1, (SELECT m FROM parameters)) AS x(j)
ORDER BY
    j, i ;

... या एक ही उद्देश्य के लिए एक समारोह का उपयोग करें, निकटवर्ती और नेस्टेड छोरों के साथ:

CREATE FUNCTION generate_up_down_series(
    _elements    /* n */ integer,
    _repetitions /* m */ integer)
RETURNS SETOF integer AS
$BODY$
declare
    j INTEGER ;
    i INTEGER ;
begin
    for j in 1 .. _repetitions loop
        for i in         1 .. _elements loop
              return next i ;
        end loop ;
        for i in reverse _elements .. 1 loop
              return next i ;
        end loop ;
    end loop ;
end ;
$BODY$
LANGUAGE plpgsql IMMUTABLE STRICT ;

मैं संभवतः मानक SQL या Transact-SQL / SQL सर्वर में समकक्ष कैसे कर सकता हूं?

जवाबों:


4

Postgres में, generate_series()फ़ंक्शन का उपयोग करना आसान है :

WITH 
  parameters (n, m) AS
  ( VALUES (3, 5) )
SELECT 
    CASE WHEN g2.i = 1 THEN gn.i ELSE p.n + 1 - gn.i END AS xi
FROM
    parameters AS p, 
    generate_series(1, p.n) AS gn (i),
    generate_series(1, 2)   AS g2 (i),
    generate_series(1, p.m) AS gm (i)
ORDER BY
    gm.i, g2.i, gn.i ;

मानक एसक्यूएल में - और यह मानकर कि एन, एम, यानी एक लाख से कम के मापदंडों पर एक उचित सीमा है - आप एक Numbersतालिका का उपयोग कर सकते हैं :

CREATE TABLE numbers 
( n int not null primary key ) ;

इसे अपने DBMS की पसंदीदा विधि से भरें:

INSERT INTO numbers (n)
VALUES (1), (2), .., (1000000) ;  -- some mildly complex SQL here
                                  -- no need to type a million numbers

और फिर इसका उपयोग करें, इसके बजाय generate_series():

WITH 
  parameters (n, m) AS
  ( VALUES (3, 5) )
SELECT 
    CASE WHEN g2.i = 1 THEN gn.i ELSE p.n + 1 - gn.i END AS xi
FROM
    parameters AS p
  JOIN numbers AS gn (i) ON gn.i <= p.n
  JOIN numbers AS g2 (i) ON g2.i <= 2
  JOIN numbers AS gm (i) ON gm.i <= p.m 
ORDER BY
    gm.i, g2.i, gn.i ;

व्यवहार में, मुझे उम्मीद नहीं है कि ये संख्या 100 से अधिक होगी; लेकिन सिद्धांत रूप में वे कुछ भी हो सकते हैं।
जोनलो

10

postgres

आप इसे एकल generate_series() और बुनियादी गणित ( गणितीय कार्यों को देखें ) के साथ काम कर सकते हैं ।

एक साधारण एसक्यूएल समारोह में लिपटे:

CREATE OR REPLACE FUNCTION generate_up_down_series(n int, m int)
  RETURNS SETOF int AS
$func$
SELECT CASE WHEN n2 < n THEN n2 + 1 ELSE n*2 - n2 END
FROM  (
   SELECT n2m, n2m % (n*2) AS n2
   FROM   generate_series(0, n*2*m - 1) n2m
   ) sub
ORDER  BY n2m
$func$  LANGUAGE sql IMMUTABLE;

कॉल करें:

SELECT * FROM generate_up_down_series(3, 4);

वांछित परिणाम उत्पन्न करता है। n और m कोई भी पूर्णांक हो सकता है जहाँ n * 2 * m अतिप्रवाह नहीं करता है int4

कैसे?

उपशम में:

  • एक साधारण आरोही संख्या के साथ पंक्तियों की वांछित कुल संख्या ( n * 2 * m ) उत्पन्न करें । मैं इसे नाम देता हूं n2m। निम्न मॉडुलो ऑपरेशन को सरल बनाने के लिए 0 से एन -1 (नॉट 1 से एन ) ।

  • N आरोही संख्याओं की एक श्रृंखला प्राप्त करने के लिए इसे % n * 2 ( %modulo ऑपरेटर) लें , m बार। मैं इसे नाम देता हूं ।n2

बाहरी क्वेरी में:

  • 1 को निचले आधे ( n2 <n ) में जोड़ें।

  • ऊपरी आधे ( n2> = n ) के लिए निचले आधे हिस्से का दर्पण n * 2 - n2 के साथ

  • मैंने ORDER BYअनुरोधित आदेश की गारंटी दी। वर्तमान संस्करणों के साथ या पोस्टग्रेज के बिना भी यह ORDER BYसरल क्वेरी के लिए काम करता है - लेकिन जरूरी नहीं कि अधिक जटिल प्रश्नों में! यह एक कार्यान्वयन विवरण है (और यह बदलने वाला नहीं है) लेकिन SQL मानक द्वारा वारंट नहीं किया गया है।

दुर्भाग्य से, generate_series()पोस्टग्रेज विशिष्ट है और मानक एसक्यूएल नहीं है, जैसा कि टिप्पणी की गई है। लेकिन हम एक ही तर्क का पुन: उपयोग कर सकते हैं:

मानक एसक्यूएल

आप generate_series()बार-बार उपयोग के लिए और अधिक कुशलता से एक पुनरावर्ती CTE के साथ सीरियल नंबर उत्पन्न कर सकते हैं, एक बार सीरियल पूर्णांक संख्याओं के साथ एक तालिका बना सकते हैं। कोई भी पढ़ सकता है, कोई भी इसे लिख सकता है!

CREATE TABLE int_seq (i integer);

WITH RECURSIVE cte(i) AS (
   SELECT 0
   UNION ALL
   SELECT i+1 FROM cte
   WHERE  i < 20000  -- or as many you might need!
   )
INSERT INTO int_seq
SELECT i FROM cte;

फिर, ऊपर SELECTभी सरल हो जाता है:

SELECT CASE WHEN n2 < n THEN n2 + 1 ELSE n*2 - n2 END AS x
FROM  (
   SELECT i, i % (n*2) AS n2
   FROM   int_seq
   WHERE  i < n*2*m  -- remember: 0 to N-1
   ) sub
ORDER  BY i;

5

अगर आपको सादे SQL की आवश्यकता है। सैद्धांतिक रूप से इसे सबसे DBMS (PostgreSQL और SQLite पर परीक्षण) पर काम करना चाहिए :

with recursive 
  s(i,n,z) as (
    select * from (values(1,1,1),(3*2,1,2)) as v  -- Here 3 is n
    union all
    select
      case z when 1 then i+1 when 2 then i-1 end, 
      n+1,
      z 
    from s 
    where n < 3), -- And here 3 is n
  m(m) as (select 1 union all select m+1 from m where m < 2) -- Here 2 is m

select n from s, m order by m, i;

व्याख्या

  1. श्रृंखला उत्पन्न करें

    ऐसा मानते हुए n=3

    with recursive s(n) as (
      select 1
      union all
      select n+1 from s where n<3
    )
    select * from s;

    यह काफी सरल है और लगभग किसी भी डॉक्स में पुनरावर्ती CTE के बारे में पाया जा सकता है। हालाँकि हमें प्रत्येक मान के दो उदाहरणों की आवश्यकता है

  2. श्रृंखला 1,1, .., n, n उत्पन्न करें

    with recursive s(n) as (
      select * from (values(1),(1)) as v
      union all
      select n+1 from s where n<3
    )
    select * from s;

    यहां हम केवल प्रारंभिक मूल्य को दोगुना कर रहे हैं, जिसमें दो पंक्तियां हैं, लेकिन दूसरे गुच्छा की हमें उल्टे क्रम में आवश्यकता है, इसलिए हम ऑर्डर को थोड़ा सा लागू करेंगे।

  3. इससे पहले कि हम आदेश का पालन करें कि यह भी एक बात है। हम तीन स्तंभों के साथ प्रारंभिक स्थिति में दो पंक्तियां रख सकते हैं, हमारा n<3अभी भी एक एकल स्तंभ सशर्त है। और, हम अभी भी मूल्य बढ़ा रहे हैं n

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(1,1,1)) as v
      union all
      select
        i,
        n+1,
        z 
      from s where n<3
    )
    select * from s;
  4. इसी तरह, हम उन्हें थोड़ा मिश्रण कर सकते हैं, हमारी प्रारंभिक स्थिति को यहां बदल सकते हैं : यहां हमारे पास एक है (6,2),(1,1)

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(6,1,2)) as v
      union all
      select
        i,
        n+1,
        z 
      from s where n<3
    )
    select * from s;
  5. श्रृंखला उत्पन्न करें। 1. एन, एन। 1

    यहां ट्रिक दो बार (1..n) सीरीज़ जेनरेट करने के लिए है, और फिर दूसरे सेट पर केवल ऑर्डरिंग को बदल दें।

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(3*2,1,2)) as v
      union all
      select
        case z when 1 then i+1 when 2 then i-1 end, 
        n+1,
        z 
      from s where n<3
    )
    select * from s order by i;

    यहाँ iआदेश है और zअनुक्रम की संख्या है (या यदि आप चाहते हैं तो अनुक्रम का आधा)। इसलिए अनुक्रम 1 के लिए हम 1 से 3 तक ऑर्डर बढ़ा रहे हैं और अनुक्रम 2 के लिए हम ऑर्डर को 6 से घटाकर 4. और अंत में घटा रहे हैं

  6. श्रृंखला को गुणा करें m

    (उत्तर में पहली क्वेरी देखें)


3

यदि आप एक पोर्टेबल समाधान चाहते हैं, तो आपको यह महसूस करना होगा कि यह मूल रूप से एक गणितीय समस्या है।

उस क्रम की संख्या के रूप में @n @ और उस क्रम में संख्या की स्थिति के रूप में @x (शून्य से शुरू) को देखते हुए, निम्न फ़ंक्शन SQL सर्वर में काम करेगा:

CREATE FUNCTION UpDownSequence
(
    @n int, -- Highest number of the sequence
    @x int  -- Position of the number we need
)
RETURNS int
AS
BEGIN
    RETURN  @n - 0.5 * (ABS((2*((@x % (@n+@n))-@n)) +1) -1)
END
GO

आप इसे इस सीटीई के साथ देख सकते हैं:

DECLARE @n int=3;--change the value as needed
DECLARE @m int=4;--change the value as needed

WITH numbers(num) AS (SELECT 0 
                      UNION ALL
                      SELECT num+1 FROM numbers WHERE num+1<2*@n*@m) 
SELECT num AS Position, 
       dbo.UpDownSequence(@n,num) AS number
FROM numbers
OPTION(MAXRECURSION 0)

(त्वरित व्याख्या: फ़ंक्शन दोहराव संख्या और ABS () को ज़िग-ज़ैग तरंग में बदलने के लिए एक अनुक्रम बनाने के लिए MODULO () का उपयोग करता है। अन्य ऑपरेशन वांछित परिणाम के साथ मिलान करने के लिए उस लहर को बदलते हैं।)


2

PostgreSQL में, यह आसान है,

CREATE OR REPLACE FUNCTION generate_up_down_series(n int, m int)
RETURNS setof int AS $$
SELECT x FROM (
  SELECT 1, ordinality AS o, x FROM generate_series(1,n) WITH ORDINALITY AS t(x)
  UNION ALL
  SELECT 2, ordinality AS o, x FROM generate_series(n,1,-1) WITH ORDINALITY AS t(x)
) AS t(o1,o2,x)
CROSS JOIN (
  SELECT * FROM generate_series(1,m)
) AS g(y)
ORDER BY y,o1,o2
$$ LANGUAGE SQL;

2

यह MS-SQL में काम करता है और मुझे लगता है कि इसे किसी भी SQL स्वाद के लिए संशोधित किया जा सकता है।

declare @max int, @repeat int, @rid int

select @max = 3, @repeat = 4

-- create a temporary table
create table #temp (row int)

--create seed rows
while (select count(*) from #temp) < @max * @repeat * 2
begin
    insert into #temp
    select 0
    from (values ('a'),('a'),('a'),('a'),('a')) as a(col1)
    cross join (values ('a'),('a'),('a'),('a'),('a')) as b(col2)
end

-- set row number can also use identity
set @rid = -1

update #temp
set     @rid = row = @rid + 1

-- if the (row/max) is odd, reverse the order
select  case when (row/@max) % 2 = 1 then @max - (row%@max) else (row%@max) + 1 end
from    #temp
where   row < @max * @repeat * 2
order by row

2

SQL सर्वर में इसे पुनरावर्ती cte का उपयोग करने का एक तरीका।

1) श्रृंखला में सदस्यों की आवश्यक संख्या उत्पन्न करें (n = 3 और m = 4 के लिए यह 24 होगा जो कि 2 * 1 * है)

2) एक caseअभिव्यक्ति में तर्क का उपयोग करने के बाद , आप आवश्यक श्रृंखला उत्पन्न कर सकते हैं।

Sample Demo

declare @n int=3;--change the value as needed
declare @m int=4;--change the value as needed

with numbers(num) as (select 1 
                      union all
                      select num+1 from numbers where num<2*@n*@m) 
select case when (num/@n)%2=0 and num%@n<>0 then num%@n 
            when (num/@n)%2=0 and num%@n=0 then 1  
            when (num/@n)%2=1 and num%@n<>0 then @n+1-(num%@n)  
            when (num/@n)%2=1 and num%@n=0 then @n
       end as num
from numbers
OPTION(MAXRECURSION 0)

जैसा कि @AndriyM ने सुझाव दिया है .. caseअभिव्यक्ति को सरल बनाया जा सकता है

with numbers(num) as (select 0
                      union all
                      select num+1 from numbers where num<2*@n*@m-1) 
select case when (num/@n)%2=0 then num%@n + 1
            when (num/@n)%2=1 then @n - num%@n
       end as num
from numbers
OPTION(MAXRECURSION 0)

Demo


2

केवल मूल गणित + - * /और मोडुलो का उपयोग करना :

SELECT x
    , s = x % (2*@n) +
         (1-2*(x % @n)) * ( ((x-1) / @n) % 2)
FROM (SELECT TOP(2*@n*@m) x FROM numbers) v(x)
ORDER BY x;

इसके लिए एक विशिष्ट SGBD की आवश्यकता नहीं है।

numbersएक संख्या तालिका होने के साथ :

...; 
WITH numbers(x) AS(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
    FROM (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n0(x)
    CROSS JOIN (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n1(x)
    CROSS JOIN (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n2(x)
)
...

यह एक पुनरावर्ती CTE का उपयोग किए बिना एक संख्या तालिका (1-1000) उत्पन्न करता है। नमूना देखें । 2 * n * m संख्या में पंक्ति की संख्या से छोटा होना चाहिए।

N = 3 और m = 4 के साथ आउटपुट:

x   s
1   1
2   2
3   3
4   3
5   2
6   1
7   1
8   2
... ...

इस संस्करण में एक छोटी संख्या तालिका (v> = n और v> = m) की आवश्यकता है:

WITH numbers(v) AS(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
    FROM (VALUES(1), (2), (3), (4), (5), (6), ...) AS n(x)
)
SELECT ord = @n*(v+2*m) + n
    , n*(1-v) + ABS(-@n-1+n)*v
FROM (SELECT TOP(@n) v FROM numbers ORDER BY v ASC) n(n)
CROSS JOIN (VALUES(0), (1)) AS s(v)
CROSS JOIN (SELECT TOP(@m) v-1 FROM numbers ORDER BY v ASC) m(m)
ORDER BY ord;

नमूना देखें ।


2

पुनरावृत्तियों का उपयोग करते हुए एक मूल कार्य।

T-SQL

create function generate_up_down_series(@max int, @rep int)
returns @serie table
(
    num int
)
as
begin

    DECLARE @X INT, @Y INT;
    SET @Y = 0;

    WHILE @Y < @REP
    BEGIN

        SET @X = 1;
        WHILE (@X <= @MAX)
        BEGIN
            INSERT @SERIE
            SELECT @X;
            SET @X = @X + 1;
        END

        SET @X = @MAX;
        WHILE (@X > 0)
        BEGIN
            INSERT @SERIE
            SELECT @X;
            SET @X = @X -1;
        END

        SET @Y = @Y + 1;
    END

    RETURN;
end
GO

postgres

create or replace function generate_up_down_series(maxNum int, rep int)
returns table (serie int) as
$body$
declare
    x int;
    y int;
    z int;
BEGIN

    x := 0;
    while x < rep loop

        y := 1;
        while y <= maxNum loop
            serie := y;
            return next;
            y := y + 1;
        end loop;

        z := maxNum;
        while z > 0 loop
            serie := z;
            return next;
            z := z - 1;
        end loop;

        x := x + 1;
    end loop;

END;
$body$ LANGUAGE plpgsql;

1
declare @n int = 5;
declare @m int = 3;
declare @t table (i int, pk int identity);
WITH  cte1 (i) 
AS ( SELECT 1
     UNION ALL
     SELECT i+1 FROM cte1
     WHERE  i < 100  -- or as many you might need!
   )
insert into @t(i) select i from cte1 where i <= @m  order by i
insert into @t(i) select i from @t order by i desc
select t.i --, t.pk, r.pk 
from @t as t 
cross join (select pk from @t where pk <= @n) as r
order by r.pk, t.pk
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.