এসকিউএল অ্যাক্সেসের ন্যূনতম ধারাবাহিক দিন নির্ধারণ করতে?


125

নিম্নলিখিত ব্যবহারকারীর ইতিহাসের সারণীতে প্রতিদিনের জন্য একটি রেকর্ড রয়েছে কোনও প্রদত্ত ব্যবহারকারী কোনও ওয়েবসাইট অ্যাক্সেস করেছেন (24 ঘন্টা ইউটিসি সময়কালে)। এটির হাজার হাজার রেকর্ড রয়েছে তবে ব্যবহারকারী প্রতি দিন কেবল একটি রেকর্ড রয়েছে। যদি ব্যবহারকারী সেই দিনের জন্য ওয়েবসাইটটিতে অ্যাক্সেস না করে থাকে তবে কোনও রেকর্ড তৈরি করা হবে না।

আইডি ইউজারআইডি ক্রিয়েশনডেট
------ ------ ------------
750997 12 2009-07-07 18: 42: 20.723
750998 15 2009-07-07 18: 42: 20.927
751000 19 2009-07-07 18: 42: 22.283

আমি যা খুঁজছি তা হ'ল ভাল পারফরম্যান্স সহ এই টেবিলের একটি এসকিউএল ক্যোয়ারী , যা আমাকে বলে যে কোন ব্যবহারকারীরা কোনও দিন বাদ না দিয়ে ক্রমাগত দিনের জন্য (এন) ওয়েবসাইট অ্যাক্সেস করেছেন।

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

এই ক্যোয়ারী এবং একটি নির্দিষ্ট স্ট্যাক ওভারফ্লো ব্যাজ মধ্যে যে কোনও সাদৃশ্য অবশ্যই খাঁটি কাকতালীয়, অবশ্যই .. :)


২৮ (<30) দিনের সদস্যতার পরে আমি উত্সাহী ব্যাজ পেয়েছি। মিস্টিসিজম।
কিরিল ভি লিয়াদভিনস্কি

3
আপনার তারিখটি ইউটিসি হিসাবে সঞ্চিত আছে? যদি তাই হয়, যদি কোনও সিএ বাসিন্দা একদিন সকাল 8 টায় এবং পরের দিন রাত 8 টায় সাইটটি পরিদর্শন করে তবে কী হবে? যদিও তিনি / তিনি প্যাসিফিক টাইম জোনে একটানা দিন পরিদর্শন করেছেন এটি ডিবি তে রেকর্ড করা হবে না কারণ ডিবি ইউটিসি হিসাবে কয়েকবার সঞ্চিত করে চলেছে।
গাই

জেফ / জারোদ - আপনি কি মেটা.স্ট্যাকেক্সেঞ্জার.কমেসেশনস / 865 /… পরীক্ষা করে দেখতে পারেন ?
রব ফারলে

উত্তর:


69

উত্তর স্পষ্টত:

SELECT DISTINCT UserId
FROM UserHistory uh1
WHERE (
       SELECT COUNT(*) 
       FROM UserHistory uh2 
       WHERE uh2.CreationDate 
       BETWEEN uh1.CreationDate AND DATEADD(d, @days, uh1.CreationDate)
      ) = @days OR UserId = 52551

সম্পাদনা করুন:

ঠিক আছে এখানে আমার গুরুতর উত্তর:

DECLARE @days int
DECLARE @seconds bigint
SET @days = 30
SET @seconds = (@days * 24 * 60 * 60) - 1
SELECT DISTINCT UserId
FROM (
    SELECT uh1.UserId, Count(uh1.Id) as Conseq
    FROM UserHistory uh1
    INNER JOIN UserHistory uh2 ON uh2.CreationDate 
        BETWEEN uh1.CreationDate AND 
            DATEADD(s, @seconds, DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate), 0))
        AND uh1.UserId = uh2.UserId
    GROUP BY uh1.Id, uh1.UserId
    ) as Tbl
WHERE Conseq >= @days

সম্পাদনা করুন:

[জেফ আতউড] এটি একটি দুর্দান্ত দ্রুত সমাধান এবং এটি গ্রহণযোগ্যতার দাবিদার, তবে রব ফারলির সমাধানটিও দুর্দান্ত এবং তর্কসাপেক্ষে আরও দ্রুত (!)। দয়া করে এটি পরীক্ষা করে দেখুন!


@ আর্টেম: এটি আমি প্রথমে ভেবেছিলাম কিন্তু যখন আমি এটি সম্পর্কে চিন্তাভাবনা করি, আপনার যদি একটি সূচক (ইউজারআইডি, ক্রিয়েশনডেট) থাকে, রেকর্ডগুলি সূচীতে ধারাবাহিকভাবে প্রদর্শিত হবে এবং এটি ভাল সম্পাদন করা উচিত।
মেহরদাদ আফশারি

এইটির জন্য উপরে যান, আমি 500k সারিগুলিতে ~ 15 সেকেন্ডে ফিরে ফলাফল পাচ্ছি।
জিম টি

4
এই সমস্ত পরীক্ষায় (শুধুমাত্র ডান দিকে বা আপনি এসআরজি হত্যা করেন) ডেটএডিএডিডি (ডিডি, ডিএটিডিএফএফ (ডিডি, 0, ক্রিয়েশন তারিখ), 0) ব্যবহার করে ক্রিয়েশনিয়ন তারিখটি কেটে দিন এটি শূন্য থেকে সরবরাহিত তারিখকে বিয়োগ করে কাজ করে - যা মাইক্রোসফ্ট এসকিউএল সার্ভার 1900-01-01 00:00:00 হিসাবে ব্যাখ্যা করে এবং দিনগুলির সংখ্যা দেয়। এরপরে এই মানটি শূন্য তারিখে পুনরায় যুক্ত করা হয় একই সময়ের সাথে একই সময় কেটে দেওয়া হয়।
অজানা

1
আমি আপনাকে যা বলতে পারি তা হল, আইডিসপোজেবলের পরিবর্তন ছাড়াই গণনাটি ভুল । আমি ব্যক্তিগতভাবে ডেটা নিজেই যাচাই করেছি। 1 দিন ফাঁক দিয়ে কিছু ব্যবহারকারী দেবেন ভুল ব্যাজ পেতে।
জেফ আতউড

3
এই ক্যোয়ারিতে 23: 59: 59.5 এ দেখা এমন কোনও ভিজিট মিস করার সম্ভাবনা রয়েছে - এটি কীভাবে পরিবর্তন ON uh2.CreationDate >= uh1.CreationDate AND uh2.CreationDate < DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate) + @days, 0)করবেন:, এর অর্থ "পরে 31 শে দিন পরে নেই" to এর অর্থ আপনি @ সেকেন্ড গণনা এড়িয়ে যেতে পারেন।
রব ফারলে

147

কীভাবে (এবং দয়া করে নিশ্চিত করুন যে পূর্ববর্তী বিবৃতিটি অর্ধ-কোলন দিয়ে শেষ হয়েছিল):

WITH numberedrows
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY UserID 
                                       ORDER BY CreationDate)
                - DATEDIFF(day,'19000101',CreationDate) AS TheOffset,
                CreationDate,
                UserID
         FROM   tablename)
SELECT MIN(CreationDate),
       MAX(CreationDate),
       COUNT(*) AS NumConsecutiveDays,
       UserID
FROM   numberedrows
GROUP  BY UserID,
          TheOffset  

ধারণাটি হ'ল আমাদের যদি দিনগুলির তালিকা থাকে (সংখ্যা হিসাবে), এবং একটি সারি_সংখ্যার, তবে মিস করা দিনগুলি এই দুটি তালিকার মধ্যে অফসেটটি কিছুটা বড় করে তোলে। সুতরাং আমরা এমন একটি সীমার সন্ধান করছি যা একটি ধারাবাহিক অফসেটযুক্ত।

আপনি এর শেষে "NumConsecلفDAY DESC বাই অর্ডার" ব্যবহার করতে পারেন, বা একটি চৌম্বক জন্য "গণনা (*)> 14" বলতে পারেন ...

যদিও আমি এটি পরীক্ষা করিনি - এটি কেবল আমার মাথার উপরের অংশে লিখে রেখেছি। আশাকরি এসকিউএল 2003 এবং এ কাজ করে।

... এবং টেবিলনামের একটি সূচক (ইউজারআইডি, ক্রিয়েশনডেট) দ্বারা খুব সাহায্য করা হবে

সম্পাদিত: টার্নস আউট অফসেটটি একটি সংরক্ষিত শব্দ, সুতরাং আমি পরিবর্তে TheOffset ব্যবহার করেছি।

সম্পাদিত: COUNT (*) ব্যবহারের পরামর্শটি খুব বৈধ - আমার এটি করা উচিত ছিল প্রথম স্থানে তবে সত্যিকার অর্থে ভাবছিলাম না। পূর্বে এটি পরিবর্তে ডেটিফ (দিন, মিনিট (ক্রিয়েশনডেট), সর্বাধিক (ক্রিয়েশনডেট) ব্যবহার করছিল।

হরণ করা


1
ওহ আপনারও যোগ করা উচিত; এর আগে ->; সহ
ম্লাদেন প্রজাদিক

2
ম্লাদেন - না, আপনার আগের বিবৃতিটি অর্ধ-কোলন দিয়ে শেষ করা উচিত। ;) জেফ - ঠিক আছে, পরিবর্তে [অফসেট] রাখুন। আমার ধারণা অফসেটটি একটি সংরক্ষিত শব্দ। যেমনটি আমি বলেছিলাম, আমি এটি পরীক্ষা করিনি।
রব ফারলে

1
কেবল নিজেকে পুনরাবৃত্তি করছি, কারণ এটি একটি প্রত্যক্ষ বিষয়। এই সমস্ত পরীক্ষায় (শুধুমাত্র ডান দিকে বা আপনি এসআরজি হত্যা করেন) ডেটএডিএডিডি (ডিডি, ডিএটিডিএফএফ (ডিডি, 0, ক্রিয়েশন তারিখ), 0) ব্যবহার করে ক্রিয়েশনিয়ন তারিখটি কেটে দিন এটি শূন্য থেকে সরবরাহিত তারিখকে বিয়োগ করে কাজ করে - যা মাইক্রোসফ্ট এসকিউএল সার্ভার 1900-01-01 00:00:00 হিসাবে ব্যাখ্যা করে এবং দিনগুলির সংখ্যা দেয়। এরপরে এই মানটি শূন্য তারিখে পুনরায় যুক্ত করা হয় একই সময়ের সাথে একই সময় কেটে দেওয়া হয়।
এআইডিस्पোজেবল

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

1
এটিকে DENSE_RANK () দিয়েও সমাধান করার বিষয়ে আমি একটি ব্লগ পোস্ট লিখেছি। tinyurl.com/denserank
রব ফারলে

18

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

এই কলামটি যুক্ত করার পরে ক্যোয়ারীটি সুস্পষ্ট হবে:

if exists(select * from table
          where LongestStreak >= 30 and UserId = @UserId)
   -- award the Woot badge.

1
+1 আমি একই রকম চিন্তাভাবনা করছিলাম তবে কিছুটা ক্ষেত্রের সাথে (ইসকনসেকটিউন) যা আগের দিনটির জন্য কোনও রেকর্ড থাকলে 1 হবে, অন্যথায় 0
ফ্রেডরিক মের্ক

7
আমরা এর জন্য স্কিমা পরিবর্তন করতে যাচ্ছি না
জেফ আতউড

এবং ইসকনস্যাক্টিউট ইউজার হিস্টরি সারণীতে সংজ্ঞায়িত একটি গণিত কলাম হতে পারে। আপনি এটিকে একটি পদার্থযুক্ত (সঞ্চিত) গণিত কলামও তৈরি করতে পারেন যা সারি সন্নিবেশ করা হলে তৈরি করা হয় IFF (যদি এবং কেবলমাত্র) আপনি সর্বদা সারণি কালানুক্রমিকভাবে সন্নিবেশ করান।
অজস্র

(কারণ কেউ কিছু একটি নির্বাচিত * করবেন, আমরা এই কম্পিউটেড কলাম জোড়ার ক্যোয়ারী পরিকল্পনা প্রভাবিত হবে না যদি না কলাম রেফারেন্সড হয় ... ডান বলছি?!?)
IDisposable

3
এটি অবশ্যই একটি বৈধ সমাধান তবে এটি যা আমি চেয়েছিলাম তা নয়। সুতরাং আমি এটিকে "থাম্বস পাশের পাশে" দিয়েছি ..
জেফ আতউড

6

এর লাইন বরাবর কিছু সুন্দরভাবে অভিব্যক্তিপূর্ণ এসকিউএল:

select
        userId,
    dbo.MaxConsecutiveDates(CreationDate) as blah
from
    dbo.Logins
group by
    userId

ধরে নিই যে আপনার কোনও ব্যবহারকারীর সংজ্ঞাবদ্ধ সামগ্রিক ফাংশন রয়েছে এর রেখাগুলিতে (সাবধান হোন এটি বগি):

using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Runtime.InteropServices;

namespace SqlServerProject1
{
    [StructLayout(LayoutKind.Sequential)]
    [Serializable]
    internal struct MaxConsecutiveState
    {
        public int CurrentSequentialDays;
        public int MaxSequentialDays;
        public SqlDateTime LastDate;
    }

    [Serializable]
    [SqlUserDefinedAggregate(
        Format.Native,
        IsInvariantToNulls = true, //optimizer property
        IsInvariantToDuplicates = false, //optimizer property
        IsInvariantToOrder = false) //optimizer property
    ]
    [StructLayout(LayoutKind.Sequential)]
    public class MaxConsecutiveDates
    {
        /// <summary>
        /// The variable that holds the intermediate result of the concatenation
        /// </summary>
        private MaxConsecutiveState _intermediateResult;

        /// <summary>
        /// Initialize the internal data structures
        /// </summary>
        public void Init()
        {
            _intermediateResult = new MaxConsecutiveState { LastDate = SqlDateTime.MinValue, CurrentSequentialDays = 0, MaxSequentialDays = 0 };
        }

        /// <summary>
        /// Accumulate the next value, not if the value is null
        /// </summary>
        /// <param name="value"></param>
        public void Accumulate(SqlDateTime value)
        {
            if (value.IsNull)
            {
                return;
            }
            int sequentialDays = _intermediateResult.CurrentSequentialDays;
            int maxSequentialDays = _intermediateResult.MaxSequentialDays;
            DateTime currentDate = value.Value.Date;
            if (currentDate.AddDays(-1).Equals(new DateTime(_intermediateResult.LastDate.TimeTicks)))
                sequentialDays++;
            else
            {
                maxSequentialDays = Math.Max(sequentialDays, maxSequentialDays);
                sequentialDays = 1;
            }
            _intermediateResult = new MaxConsecutiveState
                                      {
                                          CurrentSequentialDays = sequentialDays,
                                          LastDate = currentDate,
                                          MaxSequentialDays = maxSequentialDays
                                      };
        }

        /// <summary>
        /// Merge the partially computed aggregate with this aggregate.
        /// </summary>
        /// <param name="other"></param>
        public void Merge(MaxConsecutiveDates other)
        {
            // add stuff for two separate calculations
        }

        /// <summary>
        /// Called at the end of aggregation, to return the results of the aggregation.
        /// </summary>
        /// <returns></returns>
        public SqlInt32 Terminate()
        {
            int max = Math.Max((int) ((sbyte) _intermediateResult.CurrentSequentialDays), (sbyte) _intermediateResult.MaxSequentialDays);
            return new SqlInt32(max);
        }
    }
}

4

দেখে মনে হচ্ছে যে আপনি এন-সারিতে অবিচ্ছিন্ন থাকার জন্য n দিন ধরে অবিচ্ছিন্ন থাকার প্রয়োজনটি গ্রহণ করতে পারেন।

সুতরাং যেমন কিছু:

SELECT users.UserId, count(1) as cnt
FROM users
WHERE users.CreationDate > now() - INTERVAL 30 DAY
GROUP BY UserId
HAVING cnt = 30

হ্যাঁ, আমরা অবশ্যই এটি রেকর্ড সংখ্যার দ্বারা গেট করতে পারি .. তবে এটি কেবল কিছু সম্ভাবনা দূর করে, কারণ আমাদের বেশিরভাগ বছর ধরে 120 দিনের পরিদর্শন করতে পারত প্রচুর দৈনিক ফাঁক দিয়ে
জেফ আতউড

1
ঠিক আছে, তবে একবার আপনি এই পৃষ্ঠার পুরষ্কারটি ধরা পড়লে আপনার কেবল প্রতিদিন একবার চালানো দরকার। আমি মনে করি সেই ক্ষেত্রে, উপরের মতো কিছু কৌশলটি করবে। ধরার জন্য, আপনাকে যা করতে হবে তা হ'ল বিটওয়াইন ব্যবহার করে WHERE ধারাটি স্লাইডিং উইন্ডোতে পরিণত করা।
বিল

1
কাজের প্রতিটি রান রাষ্ট্রহীন এবং স্বতন্ত্র; এটি প্রশ্নের টেবিল ব্যতীত অন্য রান সম্পর্কে কোনও জ্ঞান নেই
জেফ আতউড

3

একটি একক এসকিউএল কোয়েরি দিয়ে এটি করা আমার পক্ষে অত্যধিক জটিল বলে মনে হচ্ছে। এই উত্তরটি আমি দুটি ভাগে ভাঙি।

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

আমরা কোয়েরি-এবং-লুপে কোড লিখতে পারি, এটাই .. সাহসী আমি বলি .. তুচ্ছ। আমি এই মুহূর্তে এসকিউএল সম্পর্কে একমাত্র উপায় সম্পর্কে কৌতূহলী।
জেফ আতউড

2

এটি যদি আপনার পক্ষে এত গুরুত্বপূর্ণ হয় তবে এই ইভেন্টটি উত্স করুন এবং আপনাকে এই তথ্য দেওয়ার জন্য একটি টেবিল চালনা করুন। এই সমস্ত ক্রেজি প্রশ্নের সাথে মেশিনটি মারার দরকার নেই।


2

আপনি একটি পুনরাবৃত্ত সিটিই (এসকিউএল সার্ভার 2005+) ব্যবহার করতে পারেন:

WITH recur_date AS (
        SELECT t.userid,
               t.creationDate,
               DATEADD(day, 1, t.created) 'nextDay',
               1 'level' 
          FROM TABLE t
         UNION ALL
        SELECT t.userid,
               t.creationDate,
               DATEADD(day, 1, t.created) 'nextDay',
               rd.level + 1 'level'
          FROM TABLE t
          JOIN recur_date rd on t.creationDate = rd.nextDay AND t.userid = rd.userid)
   SELECT t.*
    FROM recur_date t
   WHERE t.level = @numDays
ORDER BY t.userid

2

স্মারটিসের জন্য এসকিউএল-এ জো সেলকোর একটি সম্পূর্ণ অধ্যায় রয়েছে (এটি রান এবং সিকোয়েন্সগুলি বলছে)। বাড়িতে বইটি আমার কাছে নেই, তাই যখন আমি কাজ করতে যাব ... আমি আসলে এটির উত্তর দেব। (ধরে নিলাম ইতিহাসের সারণিকে ডিবো বলা হয়। ইউজারহিসটরি এবং দিনের সংখ্যাটি @ ডাইস হয়)

আর একটি লিড রান এসকিউএল টিমের ব্লগ থেকে

অন্য ধারণাটি আমার ছিল, কিন্তু এখানে কাজ করার জন্য কোনও এসকিউএল সার্ভার হাতে নেই, এটি হ'ল একটি বিভাজনযুক্ত ROW_NUMBER এর মতো সিটিই ব্যবহার করুন:

WITH Runs
AS
  (SELECT UserID
         , CreationDate
         , ROW_NUMBER() OVER(PARTITION BY UserId
                             ORDER BY CreationDate)
           - ROW_NUMBER() OVER(PARTITION BY UserId, NoBreak
                               ORDER BY CreationDate) AS RunNumber
  FROM
     (SELECT UH.UserID
           , UH.CreationDate
           , ISNULL((SELECT TOP 1 1 
              FROM dbo.UserHistory AS Prior 
              WHERE Prior.UserId = UH.UserId 
              AND Prior.CreationDate
                  BETWEEN DATEADD(dd, DATEDIFF(dd, 0, UH.CreationDate), -1)
                  AND DATEADD(dd, DATEDIFF(dd, 0, UH.CreationDate), 0)), 0) AS NoBreak
      FROM dbo.UserHistory AS UH) AS Consecutive
)
SELECT UserID, MIN(CreationDate) AS RunStart, MAX(CreationDate) AS RunEnd
FROM Runs
GROUP BY UserID, RunNumber
HAVING DATEDIFF(dd, MIN(CreationDate), MAX(CreationDate)) >= @Days

উপরেরটি সম্ভবত এটির চেয়ে আরও বড় ওয়েয়ার হতে পারে, তবে যখন আপনার খালি তারিখের চেয়ে "রান" এর কিছু অন্য সংজ্ঞা থাকে তখন এটি একটি মস্তিষ্কের সুড়সুড়ি হিসাবে রেখে যায়।


2

বেশ কয়েকটি এসকিউএল সার্ভার 2012 অপশন (নীচে এন = 100 ধরে নিচ্ছেন)।

;WITH T(UserID, NRowsPrevious)
     AS (SELECT UserID,
                DATEDIFF(DAY, 
                        LAG(CreationDate, 100) 
                            OVER 
                                (PARTITION BY UserID 
                                     ORDER BY CreationDate), 
                         CreationDate)
         FROM   UserHistory)
SELECT DISTINCT UserID
FROM   T
WHERE  NRowsPrevious = 100 

যদিও আমার নমুনা ডেটা সহ নিম্নলিখিতটি আরও কার্যকরভাবে কাজ করেছে

;WITH U
         AS (SELECT DISTINCT UserId
             FROM   UserHistory) /*Ideally replace with Users table*/
    SELECT UserId
    FROM   U
           CROSS APPLY (SELECT TOP 1 *
                        FROM   (SELECT 
                                       DATEDIFF(DAY, 
                                                LAG(CreationDate, 100) 
                                                  OVER 
                                                   (ORDER BY CreationDate), 
                                                 CreationDate)
                                FROM   UserHistory UH
                                WHERE  U.UserId = UH.UserID) T(NRowsPrevious)
                        WHERE  NRowsPrevious = 100) O

উভয়ই এই প্রতিবন্ধকতার উপর নির্ভর করে যে প্রশ্নের উত্তরে বলা হয়েছে যে প্রতিদিন ব্যবহারকারীর পক্ষে সর্বাধিক একটি রেকর্ড রয়েছে।


1

এটার মতো কিছু?

select distinct userid
from table t1, table t2
where t1.UserId = t2.UserId 
  AND trunc(t1.CreationDate) = trunc(t2.CreationDate) + n
  AND (
    select count(*)
    from table t3
    where t1.UserId  = t3.UserId
      and CreationDate between trunc(t1.CreationDate) and trunc(t1.CreationDate)+n
   ) = n

1

কারা এই সাইটটিতে ক্রমাগত অ্যাক্সেস করেছে তা সনাক্ত করতে আমি একটি সাধারণ গণিতের সম্পত্তি ব্যবহার করেছি। এই সম্পত্তিটি হ'ল আপনার অ্যাক্সেস টেবিল লগের মধ্যে প্রথমবারের অ্যাক্সেস এবং শেষ বারের রেকর্ড সংখ্যার সমান দিনের পার্থক্য থাকা উচিত।

এখানে এসকিউএল স্ক্রিপ্ট রয়েছে যা আমি ওরাকল ডিবিতে পরীক্ষা করেছি (এটি অন্যান্য ডিবিতেও কাজ করা উচিত):

-- show basic understand of the math properties 
  select    ceil(max (creation_date) - min (creation_date))
              max_min_days_diff,
           count ( * ) real_day_count
    from   user_access_log
group by   user_id;


-- select all users that have consecutively accessed the site 
  select   user_id
    from   user_access_log
group by   user_id
  having       ceil(max (creation_date) - min (creation_date))
           / count ( * ) = 1;



-- get the count of all users that have consecutively accessed the site 
  select   count(user_id) user_count
    from   user_access_log
group by   user_id
  having   ceil(max (creation_date) - min (creation_date))
           / count ( * ) = 1;

সারণী প্রস্তুতি স্ক্রিপ্ট:

-- create table 
create table user_access_log (id           number, user_id      number, creation_date date);


-- insert seed data 
insert into user_access_log (id, user_id, creation_date)
  values   (1, 12, sysdate);

insert into user_access_log (id, user_id, creation_date)
  values   (2, 12, sysdate + 1);

insert into user_access_log (id, user_id, creation_date)
  values   (3, 12, sysdate + 2);

insert into user_access_log (id, user_id, creation_date)
  values   (4, 16, sysdate);

insert into user_access_log (id, user_id, creation_date)
  values   (5, 16, sysdate + 1);

insert into user_access_log (id, user_id, creation_date)
  values   (6, 16, sysdate + 5);

1
declare @startdate as datetime, @days as int
set @startdate = cast('11 Jan 2009' as datetime) -- The startdate
set @days = 5 -- The number of consecutive days

SELECT userid
      ,count(1) as [Number of Consecutive Days]
FROM UserHistory
WHERE creationdate >= @startdate
AND creationdate < dateadd(dd, @days, cast(convert(char(11), @startdate, 113)  as datetime))
GROUP BY userid
HAVING count(1) >= @days

বিবৃতিটি cast(convert(char(11), @startdate, 113) as datetime)তারিখের সময়ের অংশটি সরিয়ে দেয় তাই আমরা মধ্যরাতে শুরু করি।

আমিও ধরে নেব যে creationdateএবং useridকলামগুলি সূচিবদ্ধ হয়।

আমি কেবল বুঝতে পেরেছি যে এটি আপনাকে সমস্ত ব্যবহারকারী এবং তাদের মোট টানা দিন বলবে না। তবে আপনাকে জানিয়ে দেবে যে কোন ব্যবহারকারীরা আপনার নির্বাচনের তারিখ থেকে একটি নির্দিষ্ট সংখ্যক দিন যাচ্ছেন।

সংশোধিত সমাধান:

declare @days as int
set @days = 30
select t1.userid
from UserHistory t1
where (select count(1) 
       from UserHistory t3 
       where t3.userid = t1.userid
       and t3.creationdate >= DATEADD(dd, DATEDIFF(dd, 0, t1.creationdate), 0) 
       and t3.creationdate < DATEADD(dd, DATEDIFF(dd, 0, t1.creationdate) + @days, 0) 
       group by t3.userid
) >= @days
group by t1.userid

আমি এটি পরীক্ষা করেছি এবং এটি সমস্ত ব্যবহারকারী এবং সমস্ত তারিখের জন্য জিজ্ঞাসা করবে। এটি স্পেন্সারের 1 ম (রসিকতা) সমাধানের উপর ভিত্তি করে তৈরি করা হয়েছে , তবে আমার কাজ করে।

আপডেট: দ্বিতীয় সমাধানে তারিখ পরিচালনার উন্নতি হয়েছে improved


ঘনিষ্ঠ, কিন্তু আমরা এমন কিছু বিষয় যা কোন (ঢ) দিনের সময়সীমা জন্য কাজ করে, একটি নির্দিষ্ট শুরুর তারিখ না প্রয়োজন
জেফ অ্যাটউড

0

এটি আপনি যা চান তা করা উচিত তবে দক্ষতার পরীক্ষার জন্য আমার কাছে পর্যাপ্ত ডেটা নেই। কনভোলিউটেড কনভার্ট / ফ্লোর স্টাফগুলি তারিখের সময় ক্ষেত্রের বাইরে থাকা অংশটি কেটে ফেলা হয়। আপনি যদি এসকিউএল সার্ভার ২০০৮ ব্যবহার করে থাকেন তবে আপনি কাস্ট ব্যবহার করতে পারেন (x.CreationDate AS তারিখ)।

INT হিসাবে @ রেঞ্জকে ডিক্লার করুন
SET @ রেঞ্জ = 10

ডিসটিন্ট ইউজারআইডি, কনভার্ট (ডেটটাইম, ফ্লোর (কনভার্ট (ফ্লাট, এ। ক্রিয়েটেশন ডেট))) নির্বাচন করুন
  TblUserLogin থেকে
যেখানে উপস্থিত
   (নির্বাচন করুন 1 
      TblUserLogin থেকে খ 
     যেখানে a.userId = b.userId 
       এবং (নির্বাচন করুন কাউন্ট (ডিসটিন্ট (কনভার্ট (ডেটটাইম, ফ্লোর (কনভার্ট (ফ্লাট, ক্রিয়েশন ডেট)))))) 
              TblUserLogin থেকে গ 
             যেখানে c.userid = b.userid 
               এবং কনভার্ট (ডেটটাইম, ফ্লোর (কনভার্ট (ফ্লাট, সি। ক্রিয়েটেশন ডেট))) নীচে কনভার্ট (তারিখ, ফ্লোর (কনভার্ট (ফ্লাট, এ। ক্রিয়েটেট))) এবং কনভার্ট (ডেটটাইম, ফ্লোর) (কনভার্ট) ) + @ রেঞ্জ -১) = @ রেঞ্জ)

তৈরির স্ক্রিপ্ট

টেবিল তৈরি করুন [ডিবিও]। [টিবিএলউজারলগিন] (
    [আইডি] [অন্তর্] পরিচয় (1,1) নকল নয়,
    [ইউজারআইডি] [ইনট্রি] নুল,
    [ক্রিয়েশন তারিখ] [তারিখের সময়] নুল
) চালু [প্রাথমিক]

বেশ নিষ্ঠুর। 406,624 সারি জুড়ে 26 সেকেন্ড।
জেফ আতউড

আপনি কতক্ষণ ব্যাজটি পুরষ্কারের জন্য পরীক্ষা করছেন? যদি এটি কেবল দিনে একবার হয় তবে ধীর সময়ের মধ্যে 26 সেকেন্ডের হিট এটি খারাপ বলে মনে হয় না। তবুও, টেবিলটি বাড়ার সাথে সাথে পারফরম্যান্স হ্রাস পাবে। প্রশ্নটি পুনরায় পড়ার পরে সময় কেটে ফেলা সময় প্রাসঙ্গিক নাও হতে পারে কারণ প্রতিদিন কেবল একটি রেকর্ড রয়েছে।
ডেভ বার্কার

0

স্পেন্সার এটি প্রায় সম্পন্ন করেছিল, তবে এটির কোডিং কোডটি হওয়া উচিত:

SELECT DISTINCT UserId
FROM History h1
WHERE (
    SELECT COUNT(*) 
    FROM History
    WHERE UserId = h1.UserId AND CreationDate BETWEEN h1.CreationDate AND DATEADD(d, @n-1, h1.CreationDate)
) >= @n

0

আমার মাথার উপরে, মাইএসকিউএলিশ:

SELECT start.UserId
FROM UserHistory AS start
  LEFT OUTER JOIN UserHistory AS pre_start ON pre_start.UserId=start.UserId
    AND DATE(pre_start.CreationDate)=DATE_SUB(DATE(start.CreationDate), INTERVAL 1 DAY)
  LEFT OUTER JOIN UserHistory AS subsequent ON subsequent.UserId=start.UserId
    AND DATE(subsequent.CreationDate)<=DATE_ADD(DATE(start.CreationDate), INTERVAL 30 DAY)
WHERE pre_start.Id IS NULL
GROUP BY start.Id
HAVING COUNT(subsequent.Id)=30

স্বীকৃত, এবং প্রায় অবশ্যই এমএসএসকিউএলের জন্য কিছু রূপান্তর প্রয়োজন, তবে আমি মনে করি এটি কিছু ধারণা দেয়।


0

ট্যালি টেবিল ব্যবহার করার বিষয়ে কীভাবে? এটি আরও অ্যালগরিদমিক পদ্ধতির অনুসরণ করে এবং কার্যকরকরণ পরিকল্পনাটি একটি বাতাস। আপনি টেবিলটি স্ক্যান করতে চান এমন 1 থেকে 'ম্যাকডেসবিহিন্ড' নাম্বার সহ ট্যালি টেবিলটিকে জনবসতি করুন (যেমন, 90 টি 3 মাস পিছনে সন্ধান করবে ইত্যাদি)।

declare @ContinousDays int
set @ContinousDays = 30  -- select those that have 30 consecutive days

create table #tallyTable (Tally int)
insert into #tallyTable values (1)
...
insert into #tallyTable values (90) -- insert numbers for as many days behind as you want to scan

select [UserId],count(*),t.Tally from HistoryTable 
join #tallyTable as t on t.Tally>0
where [CreationDate]> getdate()-@ContinousDays-t.Tally and 
      [CreationDate]<getdate()-t.Tally 
group by [UserId],t.Tally 
having count(*)>=@ContinousDays

delete #tallyTable

0

বিলের জিজ্ঞাসাটি কিছুটা টুইট করা। প্রতিদিন কেবলমাত্র একটি লগইন গণনা করার জন্য আপনাকে গ্রুপিংয়ের আগে তারিখটি কেটে যেতে পারে ...

SELECT UserId from History 
WHERE CreationDate > ( now() - n )
GROUP BY UserId, 
DATEADD(dd, DATEDIFF(dd, 0, CreationDate), 0) AS TruncatedCreationDate  
HAVING COUNT(TruncatedCreationDate) >= n

রূপান্তর (পরিবর্তে (10), ক্রিয়েশন তারিখ, 101) পরিবর্তে DATEADD (dd, DATEDIFF (dd, 0, CreationDate), 0) ব্যবহার করার জন্য সম্পাদনা করা হয়েছে।

@ আইডিপোজেবল আমি আগে ডেট পার্টটি ব্যবহার করতে চাইছিলাম তবে আমি সিনট্যাক্সটি দেখতে খুব অলস ছিলাম যাতে আমি পরিবর্তে আইডি ব্যবহার রূপান্তর করতে পারি। আমি জানি না এটির একটি উল্লেখযোগ্য প্রভাব ছিল ধন্যবাদ! এখন আমি জানি.


করার জন্য একটি SQL এর DATETIME এ ছাঁটা হচ্ছে তারিখ-শুধুমাত্র শ্রেষ্ঠ DATEADD (DD, DATEDIFF (DD, 0, UH.CreationDate), 0) সঙ্গে সম্পন্ন করা হয়
IDisposable

( উপরেরগুলি 0 টির মধ্যে পুরো দিনগুলির পার্থক্য নিয়ে কাজ করে (উদাঃ 1900-01-01 00: 00: 00.000) এবং তারপরে পুরো দিনগুলিতে এই পার্থক্যটি 0-এ ফিরে আসে (উদাঃ 1900-01-01 00:00:00) । DATETIME- র সময় অংশটি এই ফলাফলটি বাতিল করা হবে)
অদৃশ্যযোগ্য

0

এমন একটি স্কিমা ধরে নেওয়া যা এরকম হয়:

create table dba.visits
(
    id  integer not null,
    user_id integer not null,
    creation_date date not null
);

এটি ফাঁকগুলির সাথে তারিখের ক্রম থেকে সামঞ্জস্যপূর্ণ রেঞ্জগুলি বের করবে।

select l.creation_date  as start_d, -- Get first date in contiguous range
    (
        select min(a.creation_date ) as creation_date 
        from "DBA"."visits" a 
            left outer join "DBA"."visits" b on 
                   a.creation_date = dateadd(day, -1, b.creation_date ) and 
                   a.user_id  = b.user_id 
            where b.creation_date  is null and
                  a.creation_date  >= l.creation_date  and
                  a.user_id  = l.user_id 
    ) as end_d -- Get last date in contiguous range
from  "DBA"."visits" l
    left outer join "DBA"."visits" r on 
        r.creation_date  = dateadd(day, -1, l.creation_date ) and 
        r.user_id  = l.user_id 
    where r.creation_date  is null
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.