গ্যাপস এবং দ্বীপপুঞ্জ: ক্লায়েন্ট সমাধান বনাম টি-এসকিউএল কোয়েরি


10

ফাঁক এবং দ্বীপপুঞ্জের জন্য একটি টি-এসকিউএল সমাধান ক্লায়েন্টের উপর চলমান সি # সমাধানের চেয়ে দ্রুত চালানো যেতে পারে?

নির্দিষ্ট হতে, আসুন আমরা কিছু পরীক্ষার ডেটা সরবরাহ করি:

CREATE TABLE dbo.Numbers
  (
    n INT NOT NULL
          PRIMARY KEY
  ) ; 
GO 

INSERT  INTO dbo.Numbers
        ( n )
VALUES  ( 1 ) ; 
GO 
DECLARE @i INT ; 
SET @i = 0 ; 
WHILE @i < 21 
  BEGIN 
    INSERT  INTO dbo.Numbers
            ( n 
            )
            SELECT  n + POWER(2, @i)
            FROM    dbo.Numbers ; 
    SET @i = @i + 1 ; 
  END ;  
GO

CREATE TABLE dbo.Tasks
  (
    StartedAt SMALLDATETIME NOT NULL ,
    FinishedAt SMALLDATETIME NOT NULL ,
    CONSTRAINT PK_Tasks PRIMARY KEY ( StartedAt, FinishedAt ) ,
    CONSTRAINT UNQ_Tasks UNIQUE ( FinishedAt, StartedAt )
  ) ;
GO

INSERT  INTO dbo.Tasks
        ( StartedAt ,
          FinishedAt
        )
        SELECT  DATEADD(MINUTE, n, '20100101') AS StartedAt ,
                DATEADD(MINUTE, n + 2, '20100101') AS FinishedAt
        FROM    dbo.Numbers
        WHERE   ( n < 500000
                  OR n > 500005
                )
GO

এই পরীক্ষার ডেটার প্রথম সেটটির ঠিক এক ফাঁক রয়েছে:

SELECT  StartedAt ,
        FinishedAt
FROM    dbo.Tasks
WHERE   StartedAt BETWEEN DATEADD(MINUTE, 499999, '20100101')
                  AND     DATEADD(MINUTE, 500006, '20100101')

পরীক্ষার তথ্যগুলির দ্বিতীয় সেটটিতে 2M -1 ফাঁক রয়েছে, প্রতিটি দুটি সংলগ্ন বিরতির মধ্যে একটি ফাঁক:

TRUNCATE TABLE dbo.Tasks;
GO

INSERT  INTO dbo.Tasks
        ( StartedAt ,
          FinishedAt
        )
        SELECT  DATEADD(MINUTE, 3*n, '20100101') AS StartedAt ,
                DATEADD(MINUTE, 3*n + 2, '20100101') AS FinishedAt
        FROM    dbo.Numbers
        WHERE   ( n < 500000
                  OR n > 500005
                )
GO

বর্তমানে আমি ২০০৮ আর 2 চালাচ্ছি তবে 2012 এর সমাধানগুলি খুব স্বাগত welcome আমি উত্তর হিসাবে আমার সি # সমাধান পোস্ট করেছি।

উত্তর:


4

এবং একটি 1 সেকেন্ডের সমাধান ...

;WITH cteSource(StartedAt, FinishedAt)
AS (
    SELECT      s.StartedAt,
            e.FinishedAt
    FROM        (
                SELECT  StartedAt,
                    ROW_NUMBER() OVER (ORDER BY StartedAt) AS rn
                FROM    dbo.Tasks
            ) AS s
    INNER JOIN  (
                SELECT  FinishedAt,
                    ROW_NUMBER() OVER (ORDER BY FinishedAt) + 1 AS rn
                FROM    dbo.Tasks
            ) AS e ON e.rn = s.rn
    WHERE       s.StartedAt > e.FinishedAt

    UNION ALL

    SELECT  MIN(StartedAt),
        MAX(FinishedAt)
    FROM    dbo.Tasks
), cteGrouped(theTime, grp)
AS (
    SELECT  u.theTime,
        (ROW_NUMBER() OVER (ORDER BY u.theTime) - 1) / 2
    FROM    cteSource AS s
    UNPIVOT (
            theTime
            FOR theColumn IN (s.StartedAt, s.FinishedAt)
        ) AS u
)
SELECT      MIN(theTime),
        MAX(theTime)
FROM        cteGrouped
GROUP BY    grp
ORDER BY    grp

এটি আপনার অন্যান্য সমাধানের চেয়ে প্রায় 30% দ্রুত। 1 ফাঁক: (00: 00: 12.1355011 00: 00: 11.6406581), 2 এম -1 ফাঁক (00: 00: 12.4526817 00: 00: 11.7442217)। এখনও এটি সবচেয়ে খারাপ ক্ষেত্রে ক্লায়েন্ট সাইড সলিউশনের তুলনায় প্রায় 25% ধীর, ঠিক যেমনটি টুইটারে অ্যাডাম মাচানিকের পূর্বাভাস।
একে

4

নিম্নলিখিত সি # কোডটি সমস্যার সমাধান করে:

    var connString =
        "Initial Catalog=MyDb;Data Source=MyServer;Integrated Security=SSPI;Application Name=Benchmarks;";

    var stopWatch = new Stopwatch();
    stopWatch.Start();

    using (var conn = new SqlConnection(connString))
    {
        conn.Open();
        var command = conn.CreateCommand();
        command.CommandText = "dbo.GetAllTaskEvents";
        command.CommandType = CommandType.StoredProcedure;
        var gaps = new List<string>();
        using (var dr = command.ExecuteReader())
        {
            var currentEvents = 0;
            var gapStart = new DateTime();
            var gapStarted = false;
            while (dr.Read())
            {
                var change = dr.GetInt32(1);
                if (change == -1 && currentEvents == 1)
                {
                    gapStart = dr.GetDateTime(0);
                    gapStarted = true;
                }
                else if (change == 1 && currentEvents == 0 && gapStarted)
                {
                    gaps.Add(string.Format("({0},{1})", gapStart, dr.GetDateTime(0)));
                    gapStarted = false;
                }
                currentEvents += change;
            }
        }
        File.WriteAllLines(@"C:\Temp\Gaps.txt", gaps);
    }

    stopWatch.Stop();
    System.Console.WriteLine("Elapsed: " + stopWatch.Elapsed);

এই কোডটি এই সঞ্চিত পদ্ধতিটি আহ্বান করে:

CREATE PROCEDURE dbo.GetAllTaskEvents
AS 
  BEGIN ;
    SELECT  EventTime ,
            Change
    FROM    ( SELECT  StartedAt AS EventTime ,
                      1 AS Change
              FROM    dbo.Tasks
              UNION ALL
              SELECT  FinishedAt AS EventTime ,
                      -1 AS Change
              FROM    dbo.Tasks
            ) AS TaskEvents
    ORDER BY EventTime, Change DESC ;
  END ;
GO

এটি নীচের সময়ে 2M ব্যবধানে একটি ফাঁক খুঁজে খুঁজে মুদ্রণ করে, উষ্ণ ক্যাশে:

1 gap: Elapsed: 00:00:01.4852029 00:00:01.4444307 00:00:01.4644152

এটি নীচের সময়ে 2M ব্যবধানে 2M-1 ফাঁকগুলি খুঁজে খুঁজে মুদ্রণ করে, উষ্ণ ক্যাশে:

2M-1 gaps Elapsed: 00:00:08.8576637 00:00:08.9123053 00:00:09.0372344 00:00:08.8545477

এটি একটি খুব সহজ সমাধান - এটি বিকাশ করতে আমার 10 মিনিট সময় নিয়েছিল। সাম্প্রতিক এক কলেজ স্নাতক এটি নিয়ে আসতে পারেন। ডাটাবেসের দিক থেকে, এক্সিকিউশন প্ল্যান হ'ল একটি তুচ্ছ মার্জ জয় যা খুব কম সিপিইউ এবং মেমরি ব্যবহার করে।

সম্পাদনা করুন: বাস্তববাদী হতে, আমি পৃথক বাক্সে ক্লায়েন্ট এবং সার্ভার চালাচ্ছি।


হ্যাঁ, তবে রেজাল্টটি কোনও ফাইল হিসাবে নয়, ডেটাসেট হিসাবে ফিরে চাইলে আমি কী করব?
পিটার লারসন

বেশিরভাগ অ্যাপ্লিকেশনগুলি আইনিংরেবল <সুমক্লাসআরস্ট্রাক্ট> ব্যবহার করতে চায় - এই ক্ষেত্রে আমরা কেবলমাত্র তালিকার একটি লাইন যুক্ত করার পরিবর্তে প্রত্যাবর্তন করি। এই উদাহরণটি সংক্ষিপ্ত রাখতে, আমি কাঁচা পারফরম্যান্স পরিমাপ করার জন্য প্রয়োজনীয় অনেকগুলি জিনিস সরিয়ে ফেলেছি।
একে

এবং এটি সিপিইউ থেকে মুক্ত? বা এটি আপনার সমাধানের জন্য সময় যোগ করে?
পিটার লারসন

@ পিটারলারসন আপনি কি বেঞ্চমার্কের আরও ভাল উপায়ের পরামর্শ দিতে পারেন? ক্লায়েন্টের দ্বারা ডেটা লেখার ক্ষেত্রে ধীরে ধীরে ডেটা ব্যবহার করা যায়।
একে

3

আমি মনে করি এটির জন্য আমি এসকিউএল সার্ভারে আমার জ্ঞানের সীমাটি শেষ করে দিয়েছি ....

এসকিউএল সার্ভারে একটি ফাঁক খুঁজে পাওয়ার জন্য (সি # কোডটি কী করে) এবং আপনার ফাঁকগুলি শুরু করার বা শেষ করার বিষয়ে কোনও চিন্তা নেই (যারা প্রথম শুরুর আগে বা শেষ সমাপ্তির পরে), তারপরে নিম্নলিখিত কোয়েরিটি (বা রূপগুলি) হ'ল আমি দ্রুততম খুঁজে পেতে পারি:

SELECT e.FinishedAt as GapStart, s.StartedAt as GapEnd
FROM 
(
    SELECT StartedAt, ROW_NUMBER() OVER (ORDER BY StartedAt) AS rn
    FROM dbo.Tasks
) AS s
INNER JOIN  
(
    SELECT  FinishedAt, ROW_NUMBER() OVER (ORDER BY FinishedAt) + 1 AS rn
    FROM    dbo.Tasks
) AS e ON e.rn = s.rn and s.StartedAt > e.FinishedAt

যা সামান্য হাতের কাজ করে যা প্রতিটি স্টার্ট-ফিনিস সেটের জন্য, আপনি শুরুটিকে আলাদা সিকোয়েন্স হিসাবে বিবেচনা করতে পারেন এবং একের সাথে ফিনিসটি অফসেট করে এবং ফাঁকগুলি দেখানো হয়।

উদাহরণস্বরূপ (এস 1, এফ 1), (এস 2, এফ 2), (এস 3, এফ 3) নিন এবং অর্ডার করুন: 1 এস 1, এস 2, এস 3, নাল} এবং ull নাল, এফ 1, এফ 2, এফ 3} তারপরে সারি n এর সাথে N এর তুলনা করুন প্রতিটি সেটে এবং ফাঁকগুলি হ'ল এস সেট মানের তুলনায় এস সেট মানটি কম হয় ... আমি যে সমস্যাটি মনে করি তা হ'ল এসকিউএল সার্ভারে মানগুলির ক্রম অনুযায়ী সম্পূর্ণ পৃথক দুটি সেটগুলিতে যোগদানের বা তুলনা করার কোনও উপায় নেই in সেট ... সুতরাং সারি সংখ্যার উপর ভিত্তি করে নির্ভেজালভাবে একত্রীকরণের জন্য সারি_নাম্বার ফাংশনটির ব্যবহার ... তবে এসকিউএল সার্ভারকে এই মানগুলি অনন্য বলে জানানোর কোনও উপায় নেই (একটি সূচী সহ একটি টেবিল ভারে tingোকানো ছাড়াই) এটিতে - যা আরও বেশি সময় নেয় - আমি চেষ্টা করেছি), তাই আমি মনে করি যে সংযুক্তি সংযুক্তিটি সর্বোত্তমের চেয়ে কম? (যদিও আমি যা কিছু করতে পারি তার চেয়ে দ্রুত যখন এটি প্রমাণ করা শক্ত)

আমি ল্যাগ / লিড ফাংশনগুলি ব্যবহার করে সমাধান পেতে সক্ষম হয়েছি:

select * from
(
    SELECT top (100) percent StartedAt, FinishedAt, LEAD(StartedAt, 1, null) OVER (Order by FinishedAt) as NextStart
    FROM dbo.Tasks
) as x
where NextStart > FinishedAt

(যা যাইহোক, আমি ফলাফলগুলির গ্যারান্টি দিচ্ছি না - এটি কার্যকর বলে মনে হচ্ছে, তবে আমি মনে করি যে টাস্ক টেবিলের ক্রমানুসারে স্টার্টডএট উপর নির্ভর করে ... এবং এটি ধীর ছিল))

যোগফল পরিবর্তন ব্যবহার:

select * from
(
    SELECT EventTime, Change, SUM(Change) OVER (ORDER BY EventTime, Change desc ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as RunTotal --, x.*
    FROM    
    ( 
        SELECT StartedAt AS EventTime, 1 AS Change
        FROM dbo.Tasks
    UNION ALL
        SELECT  FinishedAt AS EventTime, -1 AS Change
        FROM dbo.Tasks
    ) AS TaskEvents
) as x
where x.RunTotal = 0 or (x.RunTotal = 1 and x.Change = 1)
ORDER BY EventTime, Change DESC

(আশ্চর্যের কিছু নেই, ধীর)

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

এবং কি জন্য?

একই মেশিনে চলমান, এবং সি # ডেটা এবং এসকিউএল ফিল্টার করা ডেটা একটি ফাইলে (মূল সি # কোড অনুসারে) আলাদা করে দেওয়া সময়গুলি কার্যত একই হয় .... 1 ফাঁক ডেটার জন্য প্রায় 2 সেকেন্ড (সি # সাধারণত দ্রুত) ), মাল্টি-ফাঁক ডেটা সেট (এসকিউএল সাধারণত দ্রুত) এর জন্য 8-10 সেকেন্ড।

দ্রষ্টব্য : সময় তুলনার জন্য এসকিউএল সার্ভার ডেভেলপমেন্ট এনভায়রনমেন্ট ব্যবহার করবেন না, কারণ গ্রিডে প্রদর্শন করতে সময় লাগে। যেমন এসকিউএল 2012, ভিএস2010, .NET 4.0 ক্লায়েন্ট প্রোফাইলের সাথে পরীক্ষিত

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

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

আমি যা জানি, হ্যাঁ, এসকিউএল সার্ভারটি এই ধরণের সেট তুলনাতে ভাল নয়, এবং আপনি যদি ক্যোয়ারীটি সঠিকভাবে না লিখেন তবে আপনি এর জন্য মূল্য দিতে হবে।

এটি সি # সংস্করণ লেখার চেয়ে সহজ বা শক্ত? আমি সম্পূর্ণরূপে নিশ্চিত নই, পরিবর্তন +/- 1 সম্পূর্ণ চলমান সমাধানটি সম্পূর্ণ স্বজ্ঞাত নয়, এবং আমি কিন্তু এটিই প্রথম সমাধান নয় যা একজন গড় গ্র্যাজুয়েটকে আসে ... একবার হয়ে গেলে এটি অনুলিপি করা যথেষ্ট সহজ, তবে এটি প্রথম স্থানে লিখতে অন্তর্দৃষ্টি লাগে ... এসকিউএল সংস্করণেও এটি বলা যেতে পারে। কোনটি শক্ত? দুর্বৃত্ত ডেটা বেশি শক্তিশালী কোনটি? সমান্তরাল অপারেশনের জন্য আরও সম্ভাবনা রয়েছে কোনটির? প্রোগ্রামিং প্রয়াসের সাথে তুলনা করে পার্থক্যটি এতটা কম হওয়া কি আসলেই গুরুত্বপূর্ণ?

একটি শেষ নোট; ডেটাতে একটি অস্থিতিশীল প্রতিবন্ধকতা রয়েছে - শুরু হওয়া অবশ্যই ফিনিশডএটের চেয়ে কম হওয়া উচিত , অথবা আপনি খারাপ ফলাফল পাবেন bad


3

এখানে একটি সমাধান যা 4 সেকেন্ডে চলে।

WITH cteRaw(ts, type, e, s)
AS (
    SELECT  StartedAt,
        1 AS type,
        NULL,
        ROW_NUMBER() OVER (ORDER BY StartedAt)
    FROM    dbo.Tasks

    UNION ALL

    SELECT  FinishedAt,
        -1 AS type, 
        ROW_NUMBER() OVER (ORDER BY FinishedAt),
        NULL
    FROM    dbo.Tasks
), cteCombined(ts, e, s, se)
AS (
    SELECT  ts,
        e,
        s,
        ROW_NUMBER() OVER (ORDER BY ts, type DESC)
    FROM    cteRaw
), cteFiltered(ts, grpnum)
AS (
    SELECT  ts, 
        (ROW_NUMBER() OVER (ORDER BY ts) - 1) / 2 AS grpnum
    FROM    cteCombined
    WHERE   COALESCE(s + s - se - 1, se - e - e) = 0
)
SELECT      MIN(ts) AS starttime,
        MAX(ts) AS endtime
FROM        cteFiltered
GROUP BY    grpnum;

পিটার, একটি ফাঁক দিয়ে ডেটা সেট করে এটি 10 ​​গুণ বেশি ধীর: (00: 00: 18.1016745 - 00: 00: 17.8190959) 2M-1 ফাঁকযুক্ত ডেটাতে এটি 2 গুণ ধীর: (00:00 : 17.2409640 00: 00: 17.6068879)
একে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.