সারিগুলির মধ্যে 90 দিন কেটে গেছে যেখানে পুনরাবৃত্তভাবে ফাঁকগুলি কীভাবে সন্ধান করতে হয়


17

এটি আমার সি # হোমওয়ার্ল্ডে এক ধরণের তুচ্ছ কাজ, তবে আমি এখনও এটি এসকিউএল এ তৈরি করি না এবং সেট-বেসড (কার্সার ছাড়াই) সমাধান করতে পছন্দ করব। এর ফলাফল যেমন একটি কোয়েরি থেকে আসা উচিত।

SELECT SomeId, MyDate, 
    dbo.udfLastHitRecursive(param1, param2, MyDate) as 'Qualifying'
FROM T

এটি কিভাবে কাজ করা উচিত

আমি এই তিনটি প্যারামকে একটি ইউডিএফ-তে প্রেরণ করি।
ইউডিএফ অভ্যন্তরীণভাবে কোনও দর্শন থেকে <= 90 দিনের পুরানো সারিগুলি আনতে প্যারাম ব্যবহার করে।
ইউডিএফ 'মাইডেট' অতিক্রম করে এবং যদি এটি মোট গণনায় অন্তর্ভুক্ত করা উচিত তবে 1 ফিরিয়ে দিন।
যদি এটি না করা উচিত, তবে এটি 0 ফিরে আসবে Name এখানে "যোগ্যতা" হিসাবে নামকরণ করা হয়েছে।

ইউডিএফ কী করবে

তারিখ ক্রমে সারিগুলি তালিকাভুক্ত করুন। সারিগুলির মধ্যে দিন গণনা করুন। ফলাফলটি প্রথম সারিতে হিট = ১ এ ডিফল্ট হয় যদি পার্থক্যটি 90 এর উপরে হয় - তবে ফাঁকের যোগফল 90 দিন না হওয়া পর্যন্ত পরবর্তী সারিতে পাস করুন (90 তম দিন অবশ্যই পাস করতে হবে) পৌঁছে গেলে 1 এ হিট সেট করুন এবং শূন্যস্থানটি 0 এ পুনরায় সেট করুন এটি ফলাফল থেকে সারি বাদ দেওয়ার পরিবর্তে কাজ করবে।

                                          |(column by udf, which not work yet)
Date              Calc_date     MaxDiff   | Qualifying
2014-01-01 11:00  2014-01-01    0         | 1
2014-01-03 10:00  2014-01-01    2         | 0
2014-01-04 09:30  2014-01-03    1         | 0
2014-04-01 10:00  2014-01-04    87        | 0
2014-05-01 11:00  2014-04-01    30        | 1

উপরের সারণীতে ম্যাক্সডিফ কলামটি পূর্ববর্তী লাইনের তারিখের ব্যবধান। এখনও পর্যন্ত আমার প্রচেষ্টায় সমস্যাটি হ'ল আমি উপরের নমুনায় দ্বিতীয় শেষ সারিতে অগ্রাহ্য করতে পারি না।

[সম্পাদনা]
মন্তব্য অনুসারে আমি একটি ট্যাগ যুক্ত করেছি এবং আমি এখনই সংকলন করা ইউডিএফটিও আটকান। যদিও এটি কেবল স্থানধারক এবং কার্যকর ফলাফল দেবে না।

;WITH cte (someid, otherkey, mydate, cost) AS
(
    SELECT someid, otherkey, mydate, cost
    FROM dbo.vGetVisits
    WHERE someid = @someid AND VisitCode = 3 AND otherkey = @otherkey 
    AND CONVERT(Date,mydate) = @VisitDate

    UNION ALL

    SELECT top 1 e.someid, e.otherkey, e.mydate, e.cost
    FROM dbo.vGetVisits AS E
    WHERE CONVERT(date, e.mydate) 
        BETWEEN DateAdd(dd,-90,CONVERT(Date,@VisitDate)) AND CONVERT(Date,@VisitDate)
        AND e.someid = @someid AND e.VisitCode = 3 AND e.otherkey = @otherkey 
        AND CONVERT(Date,e.mydate) = @VisitDate
        order by e.mydate
)

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

SELECT
    t.Mydate, t.VisitCode, t.Cost, t.SomeId, t.otherkey, t.MaxDiff, t.DateDiff
FROM 
(
    SELECT *,
        MaxDiff = LAST_VALUE(Diff.Diff)  OVER (
            ORDER BY Diff.Mydate ASC
                ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
    FROM 
    (
        SELECT *,
            Diff =  ISNULL(DATEDIFF(DAY, LAST_VALUE(r.Mydate) OVER (
                        ORDER BY r.Mydate ASC
                            ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING), 
                                r.Mydate),0),
            DateDiff =  ISNULL(LAST_VALUE(r.Mydate) OVER (
                        ORDER BY r.Mydate ASC
                            ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING), 
                                r.Mydate)
        FROM dbo.vGetVisits AS r
        WHERE r.VisitCode = 3 AND r.SomeId = @SomeID AND r.otherkey = @otherkey
    ) AS Diff
) AS t
WHERE t.VisitCode = 3 AND t.SomeId = @SomeId AND t.otherkey = @otherkey
    AND t.Diff <= 90
ORDER BY
    t.Mydate ASC;

মন্তব্যগুলি বর্ধিত আলোচনার জন্য নয়; এই কথোপকথন চ্যাটে সরানো হয়েছে ।
পল হোয়াইট পুনর্নির্মাণ মনিকা

উত্তর:


22

আমি প্রশ্নটি পড়তে পড়তে, প্রয়োজনীয় পুনরাবৃত্তির অ্যালগরিদমটি হ'ল:

  1. সেটে প্রথমতম তারিখ সহ সারিটি ফিরুন
  2. সেই তারিখটিকে "বর্তমান" হিসাবে সেট করুন
  3. বর্তমান তারিখের 90 দিনের বেশি পরে প্রথম তারিখের সাথে সারিটি সন্ধান করুন
  4. আর কোনও সারি না পাওয়া পর্যন্ত দ্বিতীয় ধাপ থেকে পুনরাবৃত্তি করুন

এটি একটি পুনরাবৃত্ত সাধারণ টেবিল এক্সপ্রেশন দিয়ে প্রয়োগ করা তুলনামূলকভাবে সহজ।

উদাহরণস্বরূপ, নিম্নলিখিত নমুনা ডেটা ব্যবহার (প্রশ্নের উপর ভিত্তি করে):

DECLARE @T AS table (TheDate datetime PRIMARY KEY);

INSERT @T (TheDate)
VALUES
    ('2014-01-01 11:00'),
    ('2014-01-03 10:00'),
    ('2014-01-04 09:30'),
    ('2014-04-01 10:00'),
    ('2014-05-01 11:00'),
    ('2014-07-01 09:00'),
    ('2014-07-31 08:00');

পুনরাবৃত্তির কোডটি হ'ল:

WITH CTE AS
(
    -- Anchor:
    -- Start with the earliest date in the table
    SELECT TOP (1)
        T.TheDate
    FROM @T AS T
    ORDER BY
        T.TheDate

    UNION ALL

    -- Recursive part   
    SELECT
        SQ1.TheDate
    FROM 
    (
        -- Recursively find the earliest date that is 
        -- more than 90 days after the "current" date
        -- and set the new date as "current".
        -- ROW_NUMBER + rn = 1 is a trick to get
        -- TOP in the recursive part of the CTE
        SELECT
            T.TheDate,
            rn = ROW_NUMBER() OVER (
                ORDER BY T.TheDate)
        FROM CTE
        JOIN @T AS T
            ON T.TheDate > DATEADD(DAY, 90, CTE.TheDate)
    ) AS SQ1
    WHERE
        SQ1.rn = 1
)
SELECT 
    CTE.TheDate 
FROM CTE
OPTION (MAXRECURSION 0);

ফলাফলগুলি হ'ল:

╔═════════════════════════╗
         TheDate         
╠═════════════════════════╣
 2014-01-01 11:00:00.000 
 2014-05-01 11:00:00.000 
 2014-07-31 08:00:00.000 
╚═════════════════════════╝

একটি সূচক TheDateএকটি শীর্ষস্থানীয় কী হিসাবে রয়েছে, কার্যকর করার পরিকল্পনাটি খুব দক্ষ:

হত্যা পরিকল্পনা

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

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


9

যেহেতু এটি একটি এসকিউএল সার্ভার 2014 প্রশ্ন তাই আমি একটি "কার্সর" এর স্থানীয়ভাবে সংকলিত সঞ্চিত পদ্ধতি সংস্করণ যুক্ত করতে পারি।

কিছু ডেটা সহ উত্স টেবিল:

create table T 
(
  TheDate datetime primary key
);

go

insert into T(TheDate) values
('2014-01-01 11:00'),
('2014-01-03 10:00'),
('2014-01-04 09:30'),
('2014-04-01 10:00'),
('2014-05-01 11:00'),
('2014-07-01 09:00'),
('2014-07-31 08:00');

সারণী প্রকার যা সঞ্চিত পদ্ধতির পরামিতি। যথাযথভাবে সামঞ্জস্য করুনbucket_count

create type TType as table
(
  ID int not null primary key nonclustered hash with (bucket_count = 16),
  TheDate datetime not null
) with (memory_optimized = on);

এবং একটি সঞ্চিত পদ্ধতি যা টেবিলে মূল্যবান প্যারামিটারের মধ্য দিয়ে লুপ করে এবং সারিগুলিকে সংগ্রহ করে @R

create procedure dbo.GetDates
  @T dbo.TType readonly
with native_compilation, schemabinding, execute as owner 
as
begin atomic with (transaction isolation level = snapshot, language = N'us_english', delayed_durability = on)

  declare @R dbo.TType;
  declare @ID int = 0;
  declare @RowsLeft bit = 1;  
  declare @CurDate datetime = '1901-01-01';
  declare @LastDate datetime = '1901-01-01';

  while @RowsLeft = 1
  begin
    set @ID += 1;

    select @CurDate = T.TheDate
    from @T as T
    where T.ID = @ID

    if @@rowcount = 1
    begin
      if datediff(day, @LastDate, @CurDate) > 90
      begin
        insert into @R(ID, TheDate) values(@ID, @CurDate);
        set @LastDate = @CurDate;
      end;
    end
    else
    begin
      set @RowsLeft = 0;
    end

  end;

  select R.TheDate
  from @R as R;
end

একটি মেমরি অপ্টিমাইজড টেবিল ভেরিয়েবল পূরণ করার কোড যা স্থানীয়ভাবে সংকলিত সঞ্চিত পদ্ধতির পরামিতি হিসাবে ব্যবহৃত হয় এবং পদ্ধতিটি কল করে।

declare @T dbo.TType;

insert into @T(ID, TheDate)
select row_number() over(order by T.TheDate),
       T.TheDate
from T;

exec dbo.GetDates @T;

ফলাফল:

TheDate
-----------------------
2014-07-31 08:00:00.000
2014-01-01 11:00:00.000
2014-05-01 11:00:00.000

হালনাগাদ:

যদি কোনও কারণে আপনাকে টেবিলের প্রতিটি সারিটি দেখার প্রয়োজন না হয় তবে আপনি পল হোয়াইট দ্বারা পুনরাবৃত্ত সিটিইতে প্রয়োগ করা "পরবর্তী তারিখে লাফ" সংস্করণটির সমতুল্য করতে পারেন।

ডেটা ধরণের আইডি কলামের প্রয়োজন হয় না এবং আপনার একটি হ্যাশ সূচক ব্যবহার করা উচিত নয়।

create type TType as table
(
  TheDate datetime not null primary key nonclustered
) with (memory_optimized = on);

এবং সঞ্চিত পদ্ধতিটি select top(1) ..পরবর্তী মানটি খুঁজে পেতে একটি ব্যবহার করে ।

create procedure dbo.GetDates
  @T dbo.TType readonly
with native_compilation, schemabinding, execute as owner 
as
begin atomic with (transaction isolation level = snapshot, language = N'us_english', delayed_durability = on)

  declare @R dbo.TType;
  declare @RowsLeft bit = 1;  
  declare @CurDate datetime = '1901-01-01';

  while @RowsLeft = 1
  begin

    select top(1) @CurDate = T.TheDate
    from @T as T
    where T.TheDate > dateadd(day, 90, @CurDate)
    order by T.TheDate;

    if @@rowcount = 1
    begin
      insert into @R(TheDate) values(@CurDate);
    end
    else
    begin
      set @RowsLeft = 0;
    end

  end;

  select R.TheDate
  from @R as R;
end

DATEADD এবং DATEDIFF ব্যবহার করে আপনার সমাধানগুলি প্রাথমিক ডেটাসেটের উপর নির্ভর করে বিভিন্ন ফলাফল দিতে পারে।
পাভেল নেফিয়োদভ

@ পাভেলনিফিওডভ আমি এটি দেখতে পাই না। আপনি একটি উদাহরণ দিতে বা দিতে পারেন?
মিকারেল এরিকসন

আপনি কি এটির মতো তারিখগুলিতে এটি পরীক্ষা করতে পারেন ('2014-01-01 00: 00: 00.000'), ('2014-04-01 01: 00: 00.000'), দয়া করে? আরও উত্তর আমার উত্তর পাওয়া যাবে।
পাভেল নেফিয়োদভ

@ পাভেলনিফিউডভ আহ, আমি দেখছি। আমি যদি দ্বিতীয়টি পরিবর্তন করি তবে T.TheDate >= dateadd(day, 91, @CurDate)সবার ঠিক আছে?
মিকেল এরিকসন

অথবা ওপি যথাযথ হলে, এর ডাটাটাইপ পরিবর্তন TheDateমধ্যে TTypeথেকে Date
মিকেল এরিকসন

5

একটি সমাধান যা একটি কার্সার ব্যবহার করে।
(প্রথমে কিছু প্রয়োজনীয় সারণী এবং ভেরিয়েবল) :

-- a table to hold the results
DECLARE @cd TABLE
(   TheDate datetime PRIMARY KEY,
    Qualify INT NOT NULL
);

-- some variables
DECLARE
    @TheDate DATETIME,
    @diff INT,
    @Qualify     INT = 0,
    @PreviousCheckDate DATETIME = '1900-01-01 00:00:00' ;

আসল কার্সার:

-- declare the cursor
DECLARE c CURSOR
    LOCAL STATIC FORWARD_ONLY READ_ONLY
    FOR
    SELECT TheDate
      FROM T
      ORDER BY TheDate ;

-- using the cursor to fill the @cd table
OPEN c ;

FETCH NEXT FROM c INTO @TheDate ;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @diff = DATEDIFF(day, @PreviousCheckDate, @Thedate) ;
    SET @Qualify = CASE WHEN @diff > 90 THEN 1 ELSE 0 END ;

    INSERT @cd (TheDate, Qualify)
        SELECT @TheDate, @Qualify ;

    SET @PreviousCheckDate = 
            CASE WHEN @diff > 90 
                THEN @TheDate 
                ELSE @PreviousCheckDate END ;

    FETCH NEXT FROM c INTO @TheDate ;
END

CLOSE c;
DEALLOCATE c;

এবং ফলাফলগুলি পাওয়া:

-- get the results
SELECT TheDate, Qualify
    FROM @cd
    -- WHERE Qualify = 1        -- optional, to see only the qualifying rows
    ORDER BY TheDate ;

এসকিউএলফিডেলে পরীক্ষা করা হয়েছে


এই সমাধানটির জন্য +1 করুন তবে তা নয় কারণ এটি জিনিসগুলি করার সবচেয়ে দক্ষ উপায়।
পাভেল নেফিডেভভ

@ পাভেলনিফিওডভ তাহলে আমাদের কর্মক্ষমতা পরীক্ষা করা উচিত!
ypercubeᵀᴹ 14

আমি তাতে পল হোয়াইটকে বিশ্বাস করি। পারফরম্যান্স পরীক্ষার সাথে আমার অভিজ্ঞতাটি তেমন চিত্তাকর্ষক নয়। আবার এটি আমাকে আপনার উত্তরটি দেওয়া থেকে বিরত রাখে না।
পাভেল নেফিউডভ

থ্যাঙ্কস এর ইয়পারক्यूब। সীমিত পরিমাণে সারিতে দ্রুত প্রত্যাশিত হিসাবে। 13000 সারিতে, সিটিই এবং এটি কম বা কম একই আচরণ করেছে। 130.000 সারিগুলিতে একটি 600% পার্থক্য ছিল। 13 মিনিটে এটি আমার পরীক্ষার সরঞ্জামগুলিতে 15 মিনিট পার করে। এছাড়াও আমাকে প্রাথমিক কীটি সরিয়ে ফেলতে হয়েছিল, যা সামান্য পারফরম্যান্সকে প্রভাবিত করতে পারে।
স্বতন্ত্র

পরীক্ষার জন্য Thnx। আপনি INSERT @cdকেবল তখনই সংশোধন করে পরীক্ষা করতে পারেন @Qualify=1(এবং এভাবে যদি আউটপুটে তাদের সমস্ত প্রয়োজন না হয় তবে 13 এম সারি সন্নিবেশ না করা হবে)। এবং সমাধানটি সূচকটি সন্ধান করার উপর নির্ভর করে TheDate। যদি এটি না থাকে তবে এটি কার্যকর হবে না।
ypercubeᵀᴹ

2
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[vGetVisits]') AND type in (N'U'))
DROP TABLE [dbo].[vGetVisits]
GO

CREATE TABLE [dbo].[vGetVisits](
    [id] [int] NOT NULL,
    [mydate] [datetime] NOT NULL,
 CONSTRAINT [PK_vGetVisits] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)
)

GO

INSERT INTO [dbo].[vGetVisits]([id], [mydate])
VALUES
    (1, '2014-01-01 11:00'),
    (2, '2014-01-03 10:00'),
    (3, '2014-01-04 09:30'),
    (4, '2014-04-01 10:00'),
    (5, '2014-05-01 11:00'),
    (6, '2014-07-01 09:00'),
    (7, '2014-07-31 08:00');
GO


-- Clean up 
IF OBJECT_ID (N'dbo.udfLastHitRecursive', N'FN') IS NOT NULL
DROP FUNCTION udfLastHitRecursive;
GO

-- Actual Function  
CREATE FUNCTION dbo.udfLastHitRecursive
( @MyDate datetime)

RETURNS TINYINT

AS
    BEGIN 
        -- Your returned value 1 or 0
        DECLARE @Returned_Value TINYINT;
        SET @Returned_Value=0;
    -- Prepare gaps table to be used.
    WITH gaps AS
    (
                        -- Select Date and MaxDiff from the original table
                        SELECT 
                        CONVERT(Date,mydate) AS [date]
                        , DATEDIFF(day,ISNULL(LAG(mydate, 1) OVER (ORDER BY mydate), mydate) , mydate) AS [MaxDiff]
                        FROM dbo.vGetVisits
    )

        SELECT @Returned_Value=
            (SELECT DISTINCT -- DISTINCT in case we have same date but different time
                    CASE WHEN
                     (
                    -- It is a first entry
                    [date]=(SELECT MIN(CONVERT(Date,mydate)) FROM dbo.vGetVisits))
                    OR 
                    /* 
                    --Gap between last qualifying date and entered is greater than 90 
                        Calculate Running sum upto and including required date 
                        and find a remainder of division by 91. 
                    */
                     ((SELECT SUM(t1.MaxDiff)  
                    FROM (SELECT [MaxDiff] FROM gaps WHERE [date]<=t2.[date] 
                    ) t1 
                    )%91 - 
                    /* 
                        ISNULL added to include first value that always returns NULL 
                        Calculate Running sum upto and NOT including required date 
                        and find a remainder of division by 91 
                    */
                    ISNULL((SELECT SUM(t1.MaxDiff)  
                    FROM (SELECT [MaxDiff] FROM gaps WHERE [date]<t2.[date] 
                    ) t1 
                    )%91, 0) -- End ISNULL
                     <0 )
                    /* End Running sum upto and including required date */
                    OR
                    -- Gap between two nearest dates is greater than 90 
                    ((SELECT SUM(t1.MaxDiff)  
                    FROM (SELECT [MaxDiff] FROM gaps WHERE [date]<=t2.[date] 
                    ) t1 
                    ) - ISNULL((SELECT SUM(t1.MaxDiff)  
                    FROM (SELECT [MaxDiff] FROM gaps WHERE [date]<t2.[date] 
                    ) t1 
                    ), 0) > 90) 
                    THEN 1
                    ELSE 0
                    END 
                    AS [Qualifying]
                    FROM gaps t2
                    WHERE [date]=CONVERT(Date,@MyDate))
        -- What is neccesary to return when entered date is not in dbo.vGetVisits?
        RETURN @Returned_Value
    END
GO

SELECT 
dbo.udfLastHitRecursive(mydate) AS [Qualifying]
, [id]
, mydate 
FROM dbo.vGetVisits
ORDER BY mydate 

ফলাফল

এখানে চিত্র বর্ণনা লিখুন

এসকিউএল সার্ভারে রানিং টোটাল কীভাবে গণনা করা যায় তা দেখুন

আপডেট: পারফরম্যান্স পরীক্ষার ফলাফল নীচে দেখুন।

"90 দিনের ব্যবধান" ইয়ারক्यूबের এবং আমার সমাধানগুলি অক্ষত রাখলে যদি পল হোয়াইটের সমাধানগুলিতে বিভিন্ন ফলাফল ফিরে আসতে পারে তবে সন্ধানের ক্ষেত্রে বিভিন্ন যুক্তি ব্যবহার করার কারণে। এটি যথাক্রমে DATEDIFF এবং DATEADD ফাংশন ব্যবহারের কারণে ।

উদাহরণ স্বরূপ:

SELECT DATEADD(DAY, 90, '2014-01-01 00:00:00.000')

'2014-04-01 00: 00: 00.000' অর্থ '2014-04-01 01: 00: 00.000' 90 দিনের ব্যবধান ছাড়িয়ে গেছে

কিন্তু

SELECT DATEDIFF(DAY, '2014-01-01 00:00:00.000', '2014-04-01 01:00:00.000')

'90' অর্থ ফেরৎ দেয় যে এটি এখনও ব্যবধানের মধ্যে রয়েছে।

একজন বিক্রেতার উদাহরণ বিবেচনা করুন। এই ক্ষেত্রে '2014-01-01' তারিখে '2014-01-01' 23: 59: 59: 999 'এ' 2014-01-01 'তারিখে বিক্রি হওয়া একটি পচনশীল পণ্য বিক্রি করা ঠিক আছে। এক্ষেত্রে DATEDIFF (DAY, ...) এর মান ঠিক আছে।

আরেকটি উদাহরণ হ'ল রোগীর দেখা হওয়ার অপেক্ষায়। যে কেউ '2014-01-01 00: 00: 00: 000' এ এসে '2014-01-01 23: 59: 59: 999' এ চলে যায় তার জন্য 0 (শূন্য) দিন হয় যদি ডেটাডেআইএফ ব্যবহার করা হয় তবে প্রকৃত অপেক্ষা প্রায় 24 ঘন্টা ছিল। আবার রোগী যা '2014-01-01 23:59:59' এ আসে এবং '2014-01-02 00:00:01' এ চলে যায় 'DATEDIFF ব্যবহার করা হয় তবে একদিনের জন্য অপেক্ষা করছিল।

কিন্তু আমার দ্বিমত আছে.

আমি DATEDIFF সমাধানগুলি ছেড়ে দিয়েছি এমনকি পারফরম্যান্সও সেগুলি পরীক্ষা করেছি তবে সেগুলি সত্যই তাদের নিজস্ব লিগে থাকা উচিত।

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

নিম্নলিখিত টেবিলে সমাধানগুলি পরীক্ষা করা হয়েছিল

CREATE TABLE [dbo].[vGetVisits](
    [id] [int] NOT NULL,
    [mydate] [datetime] NOT NULL,
) 

দুটি পৃথক ক্লাস্টারযুক্ত সূচক (এই ক্ষেত্রে মাইডেট) সহ:

CREATE CLUSTERED INDEX CI_mydate on vGetVisits(mydate) 
GO

সারণিটি নিম্নলিখিত উপায়ে জনবহুল ছিল

SET NOCOUNT ON
GO

INSERT INTO dbo.vGetVisits(id, mydate)
VALUES (1, '01/01/1800')
GO

DECLARE @i bigint
SET @i=2

DECLARE @MaxRows bigint
SET @MaxRows=13001

WHILE @i<@MaxRows 
BEGIN
INSERT INTO dbo.vGetVisits(id, mydate)
VALUES (@i, DATEADD(day,FLOOR(RAND()*(3)),(SELECT MAX(mydate) FROM dbo.vGetVisits)))
SET @i=@i+1
END

একটি মিলিয়ন মিলিয়ন কেসের ক্ষেত্রে INSERT এমনভাবে পরিবর্তন করা হয়েছিল যে 0-20 মিনিটের এন্ট্রি এলোমেলোভাবে যুক্ত করা হয়েছিল।

সমস্ত সমাধানগুলি সাবধানতার সাথে নিম্নলিখিত কোডটিতে মোড়ানো হয়েছিল

SET NOCOUNT ON
GO

DECLARE @StartDate DATETIME

SET @StartDate = GETDATE()

--- Code goes here

PRINT 'Total milliseconds: ' + CONVERT(varchar, DATEDIFF(ms, @StartDate, GETDATE()))

প্রকৃত কোডগুলি পরীক্ষা করা হয়েছে (কোনও নির্দিষ্ট ক্রমে নয়):

ইপারকিউবের DATEDIFF সমাধান ( YPC, DATEDIFF )

DECLARE @cd TABLE
(   TheDate datetime PRIMARY KEY,
    Qualify INT NOT NULL
);

DECLARE
    @TheDate DATETIME,
    @Qualify     INT = 0,
    @PreviousCheckDate DATETIME = '1799-01-01 00:00:00' 


DECLARE c CURSOR
    LOCAL STATIC FORWARD_ONLY READ_ONLY
    FOR
SELECT 
   mydate
FROM 
 (SELECT
       RowNum = ROW_NUMBER() OVER(PARTITION BY cast(mydate as date) ORDER BY mydate)
       , mydate
   FROM 
       dbo.vGetVisits) Actions
WHERE
   RowNum = 1
ORDER BY 
  mydate;

OPEN c ;

FETCH NEXT FROM c INTO @TheDate ;

WHILE @@FETCH_STATUS = 0
BEGIN

    SET @Qualify = CASE WHEN DATEDIFF(day, @PreviousCheckDate, @Thedate) > 90 THEN 1 ELSE 0 END ;
    IF  @Qualify=1
    BEGIN
        INSERT @cd (TheDate, Qualify)
        SELECT @TheDate, @Qualify ;
        SET @PreviousCheckDate=@TheDate 
    END
    FETCH NEXT FROM c INTO @TheDate ;
END

CLOSE c;
DEALLOCATE c;


SELECT TheDate
    FROM @cd
    ORDER BY TheDate ;

ইপারকিউবের DATEADD সমাধান ( ওয়াইপিসি, তারিখ )

DECLARE @cd TABLE
(   TheDate datetime PRIMARY KEY,
    Qualify INT NOT NULL
);

DECLARE
    @TheDate DATETIME,
    @Next_Date DATETIME,
    @Interesting_Date DATETIME,
    @Qualify     INT = 0

DECLARE c CURSOR
    LOCAL STATIC FORWARD_ONLY READ_ONLY
    FOR
  SELECT 
  [mydate]
  FROM [test].[dbo].[vGetVisits]
  ORDER BY mydate
  ;

OPEN c ;

FETCH NEXT FROM c INTO @TheDate ;

SET @Interesting_Date=@TheDate

INSERT @cd (TheDate, Qualify)
SELECT @TheDate, @Qualify ;

WHILE @@FETCH_STATUS = 0
BEGIN

    IF @TheDate>DATEADD(DAY, 90, @Interesting_Date)
    BEGIN
        INSERT @cd (TheDate, Qualify)
        SELECT @TheDate, @Qualify ;
        SET @Interesting_Date=@TheDate;
    END

    FETCH NEXT FROM c INTO @TheDate;
END

CLOSE c;
DEALLOCATE c;


SELECT TheDate
    FROM @cd
    ORDER BY TheDate ;

পল হোয়াইটের সমাধান ( পিডাব্লু )

;WITH CTE AS
(
    SELECT TOP (1)
        T.[mydate]
    FROM dbo.vGetVisits AS T
    ORDER BY
        T.[mydate]

    UNION ALL

    SELECT
        SQ1.[mydate]
    FROM 
    (
        SELECT
            T.[mydate],
            rn = ROW_NUMBER() OVER (
                ORDER BY T.[mydate])
        FROM CTE
        JOIN dbo.vGetVisits AS T
            ON T.[mydate] > DATEADD(DAY, 90, CTE.[mydate])
    ) AS SQ1
    WHERE
        SQ1.rn = 1
)

SELECT 
    CTE.[mydate]
FROM CTE
OPTION (MAXRECURSION 0);

আমার তারিখের সমাধান ( পিএন, তারিখ )

DECLARE @cd TABLE
(   TheDate datetime PRIMARY KEY
);

DECLARE @TheDate DATETIME

SET @TheDate=(SELECT MIN(mydate) as mydate FROM [dbo].[vGetVisits])

WHILE (@TheDate IS NOT NULL)
    BEGIN

        INSERT @cd (TheDate) SELECT @TheDate;

        SET @TheDate=(  
            SELECT MIN(mydate) as mydate 
            FROM [dbo].[vGetVisits]
            WHERE mydate>DATEADD(DAY, 90, @TheDate)
                    )
    END

SELECT TheDate
    FROM @cd
    ORDER BY TheDate ;

আমার DATEDIFF সমাধান ( পিএন, DATEDIFF )

DECLARE @MinDate DATETIME;
SET @MinDate=(SELECT MIN(mydate) FROM dbo.vGetVisits);
    ;WITH gaps AS
    (
       SELECT 
       t1.[date]
       , t1.[MaxDiff]
       , SUM(t1.[MaxDiff]) OVER (ORDER BY t1.[date]) AS [Running Total]
            FROM
            (
                SELECT 
                mydate AS [date]
                , DATEDIFF(day,LAG(mydate, 1, mydate) OVER (ORDER BY mydate) , mydate) AS [MaxDiff] 
                FROM 
                    (SELECT
                    RowNum = ROW_NUMBER() OVER(PARTITION BY cast(mydate as date) ORDER BY mydate)
                    , mydate
                    FROM dbo.vGetVisits
                    ) Actions
                WHERE RowNum = 1
            ) t1
    )

    SELECT [date]
    FROM gaps t2
    WHERE                         
         ( ([Running Total])%91 - ([Running Total]- [MaxDiff])%91 <0 )      
         OR
         ( [MaxDiff] > 90) 
         OR
         ([date]=@MinDate)    
    ORDER BY [date]

আমি এসকিউএল সার্ভার 2012 ব্যবহার করছি, তাই মিকেল এরিকসনের কাছে ক্ষমা চাইছি, তবে তার কোডটি এখানে পরীক্ষা করা হবে না। আমি এখনও DATADIFF এবং DATEADD এর সাথে তার সমাধানগুলি কিছু ডেটাসেটগুলিতে বিভিন্ন মান ফেরত পেতে আশা করব।

এবং আসল ফলাফলগুলি হ'ল: এখানে চিত্র বর্ণনা লিখুন


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

আপনি পরীক্ষাগুলিতে ব্যবহৃত টেবিলটি কী ছিলেন? কত সারি? সঠিক আউটপুট পেতে আপনাকে কেন তারিখ অনুসারে গ্রুপ যোগ করতে হয়েছিল তা নিশ্চিত নয়। আপনার প্রশ্নের অংশ হিসাবে আপনার তহবিল প্রকাশ করতে দ্বিধা বোধ করবেন (আপডেট)
পাভেল নেফিওডভ

ওহে! আমি আগামীকাল যোগ করব। গ্রুপটি ছিল নকল তারিখগুলি একত্রিত করতে। তবে আমি তাড়াহুড়ো করেছিলাম (গভীর রাতে) এবং সম্ভবত এটি ইতিমধ্যে রূপান্তর (তারিখ, জেড) যোগ করে সম্পন্ন হয়েছিল। সারিগুলির পরিমাণটি আমার মন্তব্যে রয়েছে। আমি আপনার সমাধান সহ 1000 সারি চেষ্টা করেছি। 12 মিনিটের প্রয়োগের সাথে 13.000 সারি চেষ্টা করেও। পলস এবং ইপারকিউবসকেও 130.000 এবং 13 মিলিয়ন মিলিয়ন টেবিলে প্রলুব্ধ করা হয়েছিল। টেবিলটি গতকাল এবং -২ বছর আগের থেকে এলোমেলো তারিখ সহ একটি সাধারণ টেবিল ছিল। তারিখের ক্ষেত্রে ক্লাস্টচারড সূচক index
স্বতন্ত্র

0

ঠিক আছে, আমি কিছু মিস করেছি বা কেন আপনি কেবল পুনরাবৃত্তি এড়িয়ে নিজের কাছে ফিরে আসবেন না? তারিখটি যদি প্রাথমিক কী হয় তবে তা অবশ্যই অনন্য হবে এবং কালক্রম অনুসারে যদি আপনি পরবর্তী সারিতে অফসেট গণনা করার পরিকল্পনা করেন

    DECLARE @T AS TABLE
  (
     TheDate DATETIME PRIMARY KEY
  );

INSERT @T
       (TheDate)
VALUES ('2014-01-01 11:00'),
       ('2014-01-03 10:00'),
       ('2014-01-04 09:30'),
       ('2014-04-01 10:00'),
       ('2014-05-01 11:00'),
       ('2014-07-01 09:00'),
       ('2014-07-31 08:00');

SELECT [T1].[TheDate]                               [first],
       [T2].[TheDate]                               [next],
       Datediff(day, [T1].[TheDate], [T2].[TheDate])[offset],
       ( CASE
           WHEN Datediff(day, [T1].[TheDate], [T2].[TheDate]) >= 30 THEN 1
           ELSE 0
         END )                                      [qualify]
FROM   @T[T1]
       LEFT JOIN @T[T2]
              ON [T2].[TheDate] = (SELECT Min([TheDate])
                                   FROM   @T
                                   WHERE  [TheDate] > [T1].[TheDate]) 

উৎপাদনের

এখানে চিত্র বর্ণনা লিখুন

যদি না আমি পুরোপুরি গুরুত্বপূর্ণ কিছু মিস করি ...


2
WHERE [TheDate] > [T1].[TheDate]90 দিনের পার্থক্য থ্রেশহোল্ডকে বিবেচনায় নিতে আপনি সম্ভবত এটি পরিবর্তন করতে চান । তবে এখনও, আপনার আউটপুটটি ওয়ান্টেড নয় one
ypercubeᵀᴹ 21

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