0

Below is the method which is working fine, but how do I convert it and use it directly inside select statement to get the desired results.

SET GLOBAL log_bin_trust_function_creators=1;
DROP FUNCTION IF EXISTS DayCount;
DELIMITER |

CREATE FUNCTION DayCount( d1 DATE, d2 DATE, daynum SMALLINT ) RETURNS INT
BEGIN
 DECLARE days INT DEFAULT 0;
  IF D1 IS NOT NULL AND D2 IS NOT NULL THEN
    WHILE D1 <= d2 DO
      BEGIN
        IF DAYOFWEEK(d1) = daynum THEN
          SET days=days+1;
        END IF;
        SET d1 = ADDDATE(d1, INTERVAL 1 DAY);
      END;
END WHILE;
END IF;
RETURN days;
END;
|
DELIMITER;

SELECT (daycount('2017-02-05','2017-02-20',7)) + (daycount('2017-02-05','2017-02-20',1)) AS 'Weekends';

For above query, I need a way to pass 7 & 1 dynamically to be able to pass non-workdays as well and remove those days from main query to calculate resolution and response time, but no function at all, direct select statement way of doing it.

UPDATED:

I have a query below, where I'm calculating time between closeDate and edate where I need to exclude non-workdays, the non-workday list is dynamic, it could be from 1 to 7 (Sunday to Saturday), my requirement is to exclude non-workdays and calculate average via mysql query itself instead of manipulating things later on via PHP:

SELECT `complains`.`id`,
DATE (edate) AS _date,
AVG((UNIX_TIMESTAMP(closeDate) - UNIX_TIMESTAMP(edate))) AS _seconds,
COUNT(*) AS totalCases
FROM `complains`
WHERE (
    (`complains`.`govt_id` = '22')
    AND (
        `complains`.`edate` BETWEEN DATE ('2016-01-01')
            AND DATE ('2017-01-01')
        )
    )
AND (`complains`.`status` = 2)
GROUP BY `_date`
ORDER BY `_date`

So workdays must be excluded between different fields i.e closeDate and edate, where edate is entrydate of a case. I'm basically calculating average resolution time of cases.

SQL Fiddle link to sql fiddle

Vipul
  • 655
  • 2
  • 6
  • 22
  • 1
    See http://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-a-very-simple-sql-query – Strawberry Feb 19 '17 at 11:42
  • 1
    Incidentally 2017-01-01 *is* a date, so there's no need to wrap it with in a function – Strawberry Feb 19 '17 at 11:46
  • @Strawberry Here is the SQL fiddle: sqlfiddle.com/#!9/b223b2/1 I need to create avg after removing non-working days. Let's suppose non working days are 1 and 7 (i.e Sunday && Saturday respectively). – Vipul Feb 19 '17 at 16:51
  • I cannot see the desired result. – Strawberry Feb 19 '17 at 19:46
  • so for the example schema and data you have in your fiddle, I can see the actual result but can you give what result do you expect for the same example schema and data :) – niceman Feb 19 '17 at 21:14
  • @Strawberry & niceman I have explained it inside gist, here is the URL: https://gist.github.com/manu1222/060bbfeeb955320e3c35e1b636719459 Explanation was huge which can't be covered here in comment, that's why added it inside gist. Please let me know if I lack in providing any information. – Vipul Feb 20 '17 at 06:24

1 Answers1

1

you can achieve what you want with an aggregation function, in particular count :

SELECT count(*) AS dayscount
FROM table
WHERE (date BETWEEN d1 AND d2) AND DAYOFWEEK(date)=daynum

Pay attention that a function is something to be used inside select statements like the DAYOFWEEK function above, they're not meant to replace select statements or vice-versa, that's because functions don't require any table, in the select statement above I had to put FROM table but in your function there is no specific table.

I would agree that it may be better to not have a function for what you want and define them when we want to transform some column(s) into something(like parsing a string column into integer or getting the difference between two dates etc).

By the way count is a function , in particular an aggregation function just like AVG,SUM,MAX,etc, your function is just a count with condition.

EDIT

regarding your update, the short answer is : you can't do it without declaring a function.

Replacing a function with a select means that data is present in some table , if I want to exclude workdays from edate to closedate then those workdays must be stored but where are they ? in what table ? for this a function is a must.

But your function doesn't have to do every thing, the query can be written like this :

SELECT id,
DATE (edate) AS _date,
AVG((NONWORKDAYSCOUNT(edate,closedate,workdaysnum))) AS _seconds,
COUNT(*) AS totalCases
FROM complains
WHERE (
    (govt_id = '22')
    AND (
        edate BETWEEN DATE ('2016-01-01')
            AND DATE ('2017-01-01')
        )
    )
AND (status = 2)
GROUP BY _date
ORDER BY _date

I'll leave NONWORKDAYSCOUNT to you , it's almost the same as DayCount you have but you need to pass daynum as "array-like" type(mysql doesn't have arrays) for which you can see this question, in particular the FIND_IN_SET is the function you need, also DayCount include the days passed in daynum not excludes them(this is either correct or wrong depending on whether you interpret daynum as workdays or non-workdays), it also needs to convert its result into seconds.

By the way you mentioned that you want to do it in mysql instead of php for performance, while your case is right, in general we don't prefer to put the CPU load on our databases and you really should measure before judging the performance.

P.S: you don't need to mention the table name everytime you want to mention a column unless you want to give a different name to your table or you join two(or more) tables which have shared columns.

You also don't need to wrap column names inside "``"

Community
  • 1
  • 1
niceman
  • 2,653
  • 29
  • 57
  • I have this query: http://pastebin.com/kG2uAY58 where I need to subtract those workdays from _seconds. Do you think above can be implemented with my query. I think while loop would still be needed. It's complicated. & dates are between two different fields, closeDate and edate. – Vipul Feb 19 '17 at 10:19
  • @Strawberry sorry, mistype :) – niceman Feb 19 '17 at 13:55
  • @niceman Sorry for any misunderstanding about my question, I have posted sqlfiddle now. – Vipul Feb 19 '17 at 16:45
  • @niceman I have added my comment, please review it. I'm not sure if you have received notification for that or not, that's why posting here. – Vipul Feb 20 '17 at 06:26
  • @ManuSharma I updated my answer, sorry I mistakenly put `DAYOFWEEK(_date)=dayname` when I should put `<>` that is "not equal"(`!=` would do the job too) – niceman Feb 20 '17 at 13:05
  • @niceman I think you are not understanding my actual requirement, the query you shared wouldn't work. My need is to check number of particular day-names between two different fields i.e closeDate and edate of each row individually then group it by edate and then take average of one single day but on top of that remove those non workdays from average before actually calculating the average. – Vipul Feb 20 '17 at 13:30
  • @niceman I think you are right, let me try that once and I will soon let you know about it and would accept your answer and change the title of the question. – Vipul Feb 21 '17 at 17:01