अद्वितीय दिनों की संख्या का पता लगाएं


11

मैं प्रत्येक कर्मचारी के लिए तालिका से अद्वितीय कार्य दिवसों की संख्या खोजने के लिए एक SQL क्वेरी लिखना चाहता हूं times

*---------------------------------------*
|emp_id  task_id  start_day   end_day   |
*---------------------------------------*
|  1        1     'monday'  'wednesday' |
|  1        2     'monday'  'tuesday'   |
|  1        3     'friday'  'friday'    |
|  2        1     'monday'  'friday'    |
|  2        1     'tuesday' 'wednesday' |
*---------------------------------------*

अपेक्षित उत्पादन:

*-------------------*
|emp_id  no_of_days |
*-------------------*
|  1        4       |
|  2        5       |
*-------------------*

मैंने क्वेरी sqlfield लिखा है जो मुझे expectedआउटपुट दे रहा है लेकिन जिज्ञासा के लिए इस क्वेरी को लिखने का एक बेहतर तरीका है? क्या मैं कैलेंडर या टैली टेबल का उपयोग कर सकता हूं?

with days_num as  
(
  select
    *,
    case 
      when start_day = 'monday' then 1
      when start_day = 'tuesday' then 2
      when start_day = 'wednesday' then 3
      when start_day = 'thursday' then 4
      when start_day = 'friday' then 5
    end as start_day_num,

    case 
      when end_day = 'monday' then 1
      when end_day = 'tuesday' then 2
      when end_day = 'wednesday' then 3
      when end_day = 'thursday' then 4
      when end_day = 'friday' then 5
    end as end_day_num

  from times
),
day_diff as
(
  select
    emp_id,
    case
      when  
        (end_day_num - start_day_num) = 0
      then
        1
      else
        (end_day_num - start_day_num)
    end as total_diff
  from days_num  
)

select emp_id,
  sum(total_diff) as uniq_working_days
from day_diff
group by
  emp_id

कोई भी सुझाव बढ़िया होंगे।


मानों के लिए (1, 1, 'monday', 'wednesday'),(1, 2, 'monday', 'tuesday'),(1, 3, 'monday', 'tuesday');empid_1 ने 3 अलग-अलग दिन (सोमवार, मंगलवार, बुधवार) काम किया है, फिडल / क्वेरी रिटर्न 4
lptr

1
@lptr यह है (1, 1, 'monday', 'wednesday'),(1, 2, 'monday', 'tuesday'),(1, 3, 'friday', 'friday');
उत्साही

3
आपकी क्वेरी वास्तव में काम नहीं करती है। आप को बदलते हैं 1 2 'monday' 'tuesday'करने के लिए 1 2 'monday' 'wednesday'परिणाम अभी भी 4 दिन होना चाहिए, लेकिन यह 5 रिटर्न
निक

जवाबों:


5

आपको मूल रूप से सप्ताह के सभी दिनों के साथ प्रत्येक emp_idपर काम किए गए दिनों के प्रतिच्छेदन को खोजने की आवश्यकता है task, और फिर अलग-अलग दिनों की गणना करें:

with days_num as (
  SELECT *
  FROM (
    VALUES ('monday', 1), ('tuesday', 2), ('wednesday', 3), ('thursday', 4), ('friday', 5)
  ) AS d (day, day_no)
),
emp_day_nums as (
  select emp_id, d1.day_no AS start_day_no, d2.day_no AS end_day_no
  from times t
  join days_num d1 on d1.day = t.start_day
  join days_num d2 on d2.day = t.end_day
)
select emp_id, count(distinct d.day_no) AS distinct_days
from emp_day_nums e
join days_num d on d.day_no between e.start_day_no and e.end_day_no
group by emp_id

आउटपुट:

emp_id  distinct_days
1       4
2       5

SQLFiddle पर डेमो


मुझे लिखते समय मुझे आपका उत्तर नहीं मिला। अब मैं देखता हूं कि मैं चीजों को जरूरत से ज्यादा जटिल बना रहा था। मुझे आपका हल पसंद है।
थोरस्टेन केटनर

2
@ThorstenKettner हाँ - मैं शुरू में खुद को पुनरावर्ती CTE पथ नीचे शुरू कर दिया, लेकिन एहसास के joinसाथ का उपयोग कर के betweenरूप में एक ही परिणाम और अधिक आसानी से प्राप्त होता है ...
निक

6

प्रश्न (फिडल) में कथन को सरल बनाने के लिए एक संभव तरीका, VALUESटेबल वैल्यू कंस्ट्रक्टर और उपयुक्त जॉइन का उपयोग करना है:

SELECT 
   t.emp_id,
   SUM(CASE 
      WHEN d1.day_no = d2.day_no THEN 1
      ELSE d2.day_no - d1.day_no
   END) AS no_of_days
FROM times t
JOIN (VALUES ('monday', 1), ('tuesday', 2), ('wednesday', 3), ('thursday', 4), ('friday', 5)) d1 (day, day_no) 
   ON t.start_day = d1.day
JOIN (VALUES ('monday', 1), ('tuesday', 2), ('wednesday', 3), ('thursday', 4), ('friday', 5)) d2 (day, day_no) 
   ON t.end_day = d2.day
GROUP BY t.emp_id

लेकिन यदि आप अलग-अलग दिनों को गिनना चाहते हैं , तो कथन अलग है। आपको सभी दिनों के बीच start_dayऔर end_dayसीमा को खोजना होगा और अलग-अलग दिनों की गणना करनी होगी :

;WITH daysCTE (day, day_no) AS (
   SELECT 'monday', 1 UNION ALL
   SELECT 'tuesday', 2 UNION ALL
   SELECT 'wednesday', 3 UNION ALL
   SELECT 'thursday', 4 UNION ALL
   SELECT 'friday', 5 
)
SELECT t.emp_id, COUNT(DISTINCT d3.day_no)
FROM times t
JOIN daysCTE d1 ON t.start_day = d1.day
JOIN daysCTE d2 ON t.end_day = d2.day
JOIN daysCTE d3 ON d3.day_no BETWEEN d1.day_no AND d2.day_no
GROUP BY t.emp_id

(ऑप्स मूल प्रश्न के साथ के रूप में) इस क्वेरी, काम नहीं करता है, जिसे आप बदलना 1 2 'monday' 'tuesday' करने के लिए 1 2 'monday' 'wednesday' परिणाम अभी भी 4 दिन होना चाहिए, लेकिन यह 5. रिटर्न
निक

@ अच्छा, क्षमा करें, मैं नहीं समझ सकता। ओपी स्पष्टीकरण के आधार पर, के बीच mondayऔर 2 दिन हैं wednesday। क्या मैं कुछ भूल रहा हूँ?
झोरोव

इनपुट डेटा को बदल दें जैसा कि मैंने वर्णित किया है, और आपकी क्वेरी 5 लौटती है। हालांकि जवाब अभी भी 4 होना चाहिए क्योंकि अभी भी 4 अद्वितीय दिन काम कर रहे हैं।
निक

@ अच्छा, अब मैं आपकी बात समझ गया हूं। लेकिन अगर मैं ओपी फिडेल में मूल्यों को बदलता हूं, तो परिणाम होगा 5, नहीं 4। यह उत्तर सिर्फ और अधिक सरल कथन बताता है। धन्यवाद।
झोरोव

OPs क्वेरी भी गलत है। सही है कि डेटा के साथ जवाब 4 है, के रूप में वहाँ केवल 4 अद्वितीय दिनों कर रहे हैं।
निक

2

आपकी क्वेरी सही नहीं है। बुधवार से गुरुवार तक सोमवार से मंगलवार तक कोशिश करें। इसका परिणाम 4 दिनों में होना चाहिए, लेकिन आपकी क्वेरी 2 दिनों की है। आपकी क्वेरी यह भी नहीं बताती है कि दो सीमाएँ आसन्न हैं या अतिव्यापी हैं और न ही।

इसे हल करने का एक तरीका एक सीमा से सभी दिनों को प्राप्त करने के लिए एक पुनरावर्ती CTE लिखना है और फिर अलग-अलग दिनों की गणना करना है।

with weekdays (day_name, day_number) as
(
  select * from (values ('monday', 1), ('tuesday', 2), ('wednesday', 3),
                        ('thursday', 4), ('friday', 5)) as t(x,y)
)
, emp_days(emp_id, day, last_day)
as
(
  select emp_id, wds.day_number, wde.day_number
  from times t
  join weekdays wds on wds.day_name = t.start_day
  join weekdays wde on wde.day_name = t.end_day
  union all
  select emp_id, day + 1, last_day
  from emp_days
  where day < last_day
)
select emp_id, count(distinct day)
from emp_days
group by emp_id
order by emp_id;

डेमो: http://sqlfiddle.com/# -18/ 4a5ac/16

(जैसा कि देखा जा सकता है कि मैं मूल्यों के निर्माता को सीधे रूप में लागू नहीं कर सकता with weekdays (day_name, day_number) as (values ('monday', 1), ...)। मुझे नहीं पता कि क्यों है। क्या यह SQL सर्वर या मुझे है? खैर, अतिरिक्त चयन के साथ यह काम करता है :-)


2
with cte as 
(Select id, start_day as day
   group by id, start_day
 union 
 Select id, end_day as day
   group by id, end_day
)

select id, count(day)
from cte
group by id

3
कोड केवल जवाब लगभग हमेशा कैसे और क्यों वे काम के कुछ स्पष्टीकरण के अलावा द्वारा सुधार किया जा सकता है।
जेसन एलेर

1
ढेर अतिप्रवाह में आपका स्वागत है! हालांकि यह कोड प्रश्न को हल कर सकता है, जिसमें यह भी बताया गया है कि यह समस्या कैसे और क्यों हल करती है, इससे वास्तव में आपके पोस्ट की गुणवत्ता को बेहतर बनाने में मदद मिलेगी, और शायद अधिक वोट भी मिलेंगे। याद रखें कि आप भविष्य में पाठकों के लिए सवाल का जवाब दे रहे हैं, न कि केवल उस व्यक्ति से जो अब पूछ रहा है। स्पष्टीकरण जोड़ने के लिए कृपया अपना उत्तर संपादित करें और संकेत दें कि क्या सीमाएँ और मान्यताएँ लागू होती हैं। समीक्षा से
डबल-बीप

1
declare @times table
(
  emp_id int,
  task_id int,
  start_day varchar(50),
  end_day varchar(50)
);

insert into @times(emp_id, task_id, start_day, end_day)
values
(1, 1, 'monday', 'wednesday'),
(1, 2, 'monday', 'tuesday'),
(1, 3, 'friday', 'friday'),
--
(2, 1, 'monday', 'friday'),
(2, 2, 'tuesday', 'wednesday'),
--
(3, 1, 'monday', 'wednesday'),
(3, 2, 'monday', 'tuesday'),
(3, 3, 'monday', 'tuesday');

--for sql 2019, APPROX_COUNT_DISTINCT() eliminates distinct sort (!!)...
-- ...with a clustered index on emp_id (to eliminate the hashed aggregation) the query cost gets 5 times cheaper ("overlooking" the increase in memory) !!??!!
/*
select t.emp_id, APPROX_COUNT_DISTINCT(v.val) as distinctweekdays
from
(
select *, .........
*/


select t.emp_id, count(distinct v.val) as distinctweekdays
from
(
select *, 
case start_day when 'monday' then 1
      when 'tuesday' then 2
      when 'wednesday' then 3
      when 'thursday' then 4
      when 'friday' then 5
    end as start_day_num,
case end_day when 'monday' then 1
      when 'tuesday' then 2
      when 'wednesday' then 3
      when 'thursday' then 4
      when 'friday' then 5
    end as end_day_num
from @times
) as t
join (values(1),(2), (3), (4), (5)) v(val) on v.val between t.start_day_num and t.end_day_num
group by t.emp_id;

1
आपसे अनुरोध है कि आप अपने कोड का विवरण लिखें कि यह कैसे काम करता है?
सूरज कुमार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.