SQL server pivot table for time keeping - sql-server-2008

I have a time keeping table that records the following data:
+----------------+-------------------------+-------+
| TK_EMPLOYEE_ID | TK_DATETIME | TK_ID |
+----------------+-------------------------+-------+
| 101 | 2013-09-30 08:01:54.000 | 1 |
| 101 | 2013-09-30 12:02:16.000 | 2 |
| 101 | 2013-09-30 12:30:12.000 | 3 |
| 101 | 2013-09-30 16:31:02.000 | 4 |
| 101 | 2013-10-01 08:33:59.000 | 5 |
| 101 | 2013-10-01 12:05:59.000 | 6 |
| 101 | 2013-10-01 12:30:29.000 | 7 |
| 101 | 2013-10-01 16:40:48.000 | 8 |
| 102 | 2013-10-01 08:00:48.000 | 9 |
| 102 | 2013-10-01 12:00:48.000 | 10|
+----------------+-------------------------+-------+
The clock entries are taken so that the odd scans are "CLOCK IN" and the even are "CLOCK OUT". So in the table above we can see that the employee clocked in at 8:01am, clocked out at 12:02; clocked back in from lunch at 12:30 and left work and 16:31.
How could I format display this in a SQL pivot table to display something like the following detailing entries for each day of the week?
EMPID | Mon | Tue | Wed
101 | 8:01 - 12:02 (4:01) | 08:33 - 12:05 (03:32) | etc
| 12:30- 16:31 (4:01) | 12:30 - 16:40 (04:10 |
102 | 8:00- 12:00 (4:00)
The time difference between the two times is shown in brackets.
I plan on using SSRS to display the results.

Related

MYSQL: Display one query from a select statement from two unrelated tables

I'm trying to display the sum of monthly revenue from different unrelated tables, Where i display the budgeted against the actual usage
I have two tables Planned Budget and actual budget, with with similar columns but different data, in a query i want retrieve the sum of budget and group it Yearly and monthly.
Planned Budget table
+-----+-------+-------------+-------------+
| uid | codes | opca_budget | date_period |
+-----+-------+-------------+-------------+
| 10 | 3210 | 3000 | 2018-03-01 |
| 17 | 3355 | 3000 | 2018-03-01 |
| 33 | 3210 | 4000 | 2018-04-01 |
| 40 | 3355 | 4000 | 2018-04-01 |
| 56 | 3210 | 5000 | 2018-05-01 |
| 63 | 3355 | 5000 | 2018-05-01 |
| 79 | 3210 | 6000 | 2018-06-01 |
| 86 | 3355 | 6000 | 2018-06-01 |
| 109 | 3355 | 45000 | 2018-07-01 |
Actual Budget
+-----+-------+-------------+---------------------+
| uid | codes | opca_budget | date_period |
+-----+-------+-------------+---------------------+
| 10 | 3210 | 6500 | 2018-03-01 00:00:00 |
| 17 | 3355 | 6500 | 2018-03-01 00:00:00 |
| 18 | 3120 | 6500 | 2018-03-01 00:00:00 |
| 33 | 3210 | 7500 | 2018-04-01 00:00:00 |
| 40 | 3355 | 7500 | 2018-04-01 00:00:00 |
| 41 | 3120 | 7500 | 2018-04-01 00:00:00 |
| 56 | 3210 | 8500 | 2018-05-01 00:00:00 |
| 63 | 3355 | 8500 | 2018-05-01 00:00:00 |
| 64 | 3120 | 8500 | 2018-05-01 00:00:00 |
I tried to combine them horizontally, But i get wrong results
SELECT YEAR(c.date_period)
, MONTH(c.date_period)
, SUM(c.opca_budget) MonthlyBudget
, SUM(a.opca_budget) MonthlyUsage
FROM opexcapex c
LEFT
JOIN opxcpx_actuals a
ON YEAR(c.date_period) = YEAR(a.date_period)
AND MONTH(c.date_period) = MONTH(a.date_period)
WHERE c.date_period BETWEEN '2018-03-01' AND '2019-02-29'
GROUP
BY YEAR(c.date_period)
, MONTH(c.date_period);
+------+-------+---------------+--------------+
| Year | Month | MonthlyBudget | MonthlyUsage |
+------+-------+---------------+--------------+
| 2018 | 3 | 168000 | 364000 |
| 2018 | 4 | 224000 | 420000 |
| 2018 | 5 | 280000 | 476000 |
| 2018 | 6 | 336000 | 532000 |
| 2018 | 7 | 2520000 | 588000 |
| 2018 | 8 | 576000 | 367200 |
| 2018 | 9 | 3240000 | 367200 |
| 2018 | 10 | 720000 | 252000 |
| 2018 | 11 | 792000 | 1807200 |
| 2018 | 12 | 2583000 | 1323000 |
| 2019 | 1 | 9000 | NULL |
| 2019 | 2 | 36000 | NULL |
+------+-------+---------------+--------------+
I expect to get results like this:
+-----------+-------------+---------------+--------------+
| BudgtYear | BudgetMonth | MonthlyBudget | MonthlyUsage |
+-----------+-------------+---------------+--------------+
| 2018 | 3 | 24000 | 45500 |
| 2018 | 4 | 32000 | 52500 |
| 2018 | 5 | 40000 | 59500 |
| 2018 | 6 | 48000 | 66500 |
| 2018 | 7 | 360000 | 73500 |
| 2018 | 8 | 72000 | 40800 |
| 2018 | 9 | 405000 | 40800 |
| 2018 | 10 | 90000 | 28000 |
| 2018 | 11 | 99000 | 200800 |
| 2018 | 12 | 369000 | 147000 |
| 2019 | 1 | 9000 | |
| 2019 | 2 | 36000 | |
+-----------+-------------+---------------+--------------+
You want to do a UNION ALL as a sub-query and then calculate the sum on the value from that sub-query, notice also that I use two amount columns in the union but one is always null, this is to have the two selects return the same number of columns but separated values
SELECT YEAR(date_period) as year, MONTH(date_period) as month, SUM(mbudget) AS MonthlyBudget, SUM(musage) as MonthlyUsage
FROM (SELECT date_period, opca_budget mbudget, null musage
FROM opexcapex
UNION ALL
SELECT date_period, null, opca_budget
FROM opxcpx_actuals
) sq
WHERE date_period BETWEEN '2018-03-01' AND '2019-02-29'
GROUP BY year, month
You should be able to UNION the 2 tables first, then SELECT from the resulting data, something like this (Updated query) :-
SELECT YEAR(date_period), MONTH(date_period), MonthlyBudget, MonthlyUsage
FROM
(SELECT date_period, opca_budget as MonthlyBudget, 0 as MonthlyUsage FROM
opexcapex
UNION
SELECT date_period, 0 as MonthlyBudget, opca_budget as MonthlyUsage FROM
opxcpx_actuals) T1
WHERE date_period BETWEEN '2018-03-01' AND '2019-02-29'
GROUP BY YEAR(date_period), MONTH(date_period);

difference between dates excluding custom periods

Good morning,
I need a solution for counting days between specific dates. In addition I need to exclude specific dates (public holidays and weekends), which is easy and there are plenty of instructions on how to do it.
But I need to accomplish one more thing. For every person, I also have custom vacation periods and I need them to be subtracted from the previous result.
At the moment, i have two tables. One for the custom vacation periods:
+-----+----------+---------------------+---------------------+------+
| id | employee | start | end | away |
+-----+----------+---------------------+---------------------+------+
| 835 | 2.3 | 2016-12-05 00:00:00 | 2016-12-10 00:00:00 | P |
| 836 | 5.3.5.1 | 2017-01-03 00:00:00 | 2017-01-23 00:00:00 | P |
| 837 | 5.3.5.6 | 2016-12-21 00:00:00 | 2017-04-01 00:00:00 | P |
| 838 | 5.3.3.1 | 2017-01-01 00:00:00 | 2017-01-03 00:00:00 | P |
| 839 | 5.3.1.2 | 2017-01-03 00:00:00 | 2017-01-12 00:00:00 | P |
| 840 | 5.3.1.6 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 | P |
| 841 | 5.1.7 | 2017-01-09 00:00:00 | 2017-01-15 00:00:00 | P |
| 842 | 2.2 | 2017-02-16 00:00:00 | 2017-02-26 00:00:00 | P |
| 843 | 2.5 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 844 | 2.5 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 845 | 2.5 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 846 | 2.4 | 2017-04-04 00:00:00 | 2017-04-08 00:00:00 | P |
+-----+----------+---------------------+---------------------+------+
12 rows in set (0.00 sec)
The reason I have date and time on start and end is that the table actually has info about their working schedule also. The vacation schedule is marked by column 'away' set as 'P'.
The second table looks like this:
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | varchar(45) | YES | | NULL | |
| dayofweek | varchar(45) | YES | | NULL | |
| short | varchar(45) | YES | | NULL | |
| holiday | varchar(45) | YES | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
This table is basically a calendar with additional information about what day of week it is (i know that this can be done with dayofweek() but i need this column also), 'short' determines whether it's a shortened workday (essential when calculating hours because there are a couple of days in a year when the working day is shorter) and 'holiday' which marks public holidays.
I am trying to figure out how to calculate working days during one month (01.08.2017 - 31.08.2017) by subtracting public holidays and weekends (which I can do and is relatively easy) and also subtract custom period of vacations. The main problem is that for some people, there can be more than one period of absence during one month.
If you look at the example above, employee 2.5 has a vacation period that starts in July and ends in August and I need to subtract only the amount in that period that is in August. Employee 2.5 has another vacation in August and I need to subtract this also. In addition to that, this employee has another vacation from June to July.
The perfect outcome would be something like this:
+----------+-------+---------------+--------------+---------------+
| employee | month | working hours | working days | vacation days |
+----------+-------+---------------+--------------+---------------+
The only solution I am able to come up with is to create a new table called vacations with a column for each day and each row shows data for one employee. Or the other way around where employees are columns and rows are dates. But I would not like to start playing with this idea before i know that there isn't an easier way to do it.
Once again, I am sure that there is an easy function in mysql for this but I fail to figure it out.
To calculate working hours do this:
$to_time = strtotime("$row[2]");
$from_time = strtotime("$row[3]");
$diff = $to_time - $from_time
echo round(abs($diff) / 3600,2). " hour(s)";
Where $row[2] & $rows[3] are your start and end dates. They /3600 is for calculating hours, you can get minutes by just writing 60 instead of 3600.
As for the working days and vacations, you can set up a counter and increment it for days and decrement it for vacations using the $diff variable. Count the number of hours or days you like and then increment/decrement it the way you like.
I tidied it up a bit. I finally got what I needed.
At first I changed the calendar table:
mysql> select id,date,workday from calendar where date between '2017-06-20' and '2017-06-30';
+-----+------------+---------+
| id | date | workday |
+-----+------------+---------+
| 202 | 2017-06-20 | 1 |
| 203 | 2017-06-21 | 1 |
| 204 | 2017-06-22 | 0.625 |
| 205 | 2017-06-23 | Holiday |
| 206 | 2017-06-24 | Holiday |
| 207 | 2017-06-25 | NULL |
| 208 | 2017-06-26 | 1 |
| 209 | 2017-06-27 | 1 |
| 210 | 2017-06-28 | 1 |
| 211 | 2017-06-29 | 1 |
| 212 | 2017-06-30 | 1 |
+-----+------------+---------+
11 rows in set (0.00 sec)
1 in workday column says it's an ordinary workday, NULL means that it is not (weekend) and 0.625 is shortened workday (5/8=0,625).a shortened working day is 5 hours. This basically means if i sum the workday column and multiply it with the length of an ordinary workday, I get the normal working hours according to the law between selected period of time.
To do this, at the moment i just set up some variables, this is just a work in progress:
set #START='2016-12-01';
set #END='2016-12-31';
set #WORKDAY=8;
set #norm=(select sum(workday)*#workday from calendar where date between #start and #end);
So the variables #START and #END give me the period i am interested in. #NORM will give me the normal working hours if one is not on vacation or sick leave between #START and #END.
I added some generic data to schedule:
mysql> select id,position,start,end,away from schedule where away='P';
+-----+----------+---------------------+---------------------+------+
| id | position | start | end | away |
+-----+----------+---------------------+---------------------+------+
| 835 | 2.3 | 2016-12-05 00:00:00 | 2016-12-10 00:00:00 | P |
| 836 | 5.3.5.1 | 2017-01-03 00:00:00 | 2017-01-23 00:00:00 | P |
| 837 | 5.3.5.6 | 2016-12-21 00:00:00 | 2017-04-01 00:00:00 | P |
| 838 | 5.3.3.1 | 2017-01-01 00:00:00 | 2017-01-03 00:00:00 | P |
| 839 | 5.3.1.2 | 2017-01-03 00:00:00 | 2017-01-12 00:00:00 | P |
| 840 | 5.3.1.6 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 | P |
| 841 | 5.1.7 | 2017-01-09 00:00:00 | 2017-01-15 00:00:00 | P |
| 842 | 2.2 | 2017-02-16 00:00:00 | 2017-02-26 00:00:00 | P |
| 843 | 2.5 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 844 | 2.5 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 845 | 2.5 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 846 | 2.4 | 2017-04-04 00:00:00 | 2017-04-08 00:00:00 | P |
| 847 | 2.3 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 848 | 2.3 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 849 | 2.3 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 850 | 2.5 | 2017-08-29 00:00:00 | 2017-09-15 00:00:00 | P |
| 851 | 2.3 | 2017-08-29 00:00:00 | 2017-09-15 00:00:00 | P |
| 852 | 2.1 | 2016-11-15 00:00:00 | 2016-12-20 00:00:00 | P |
| 853 | 2.2 | 2016-11-10 00:00:00 | 2017-01-15 00:00:00 | P |
+-----+----------+---------------------+---------------------+------+
19 rows in set (0.00 sec)
And to get the info about each employees specific working I use following select:
SELECT position,#NORM AS normhrs,#NORM - (SUM(vac_workday))*#WORKDAY AS wrkhrs
FROM
(
SELECT position,holiday_start,holiday_end,
(SELECT SUM(workday) FROM calendar WHERE date BETWEEN holiday_start AND holiday_end) AS vac_workday
FROM
(
SELECT position,
DATE_FORMAT(CASE WHEN start<=#START THEN #START ELSE start END,'%Y-%m-%d') AS 'holiday_start',
DATE_FORMAT(CASE WHEN end>=#END THEN #END ELSE end END,'%Y-%m-%d') AS 'holiday_end'
FROM schedule WHERE away IS NOT NULL AND DATE_FORMAT(CASE WHEN start<=#START AND end>=#START THEN #START
WHEN start>=#START AND start<=#END THEN start
ELSE NULL end,'%Y-%m')=DATE_FORMAT(#START,'%Y-%m')
)
AS outertable
)
AS outertable2 GROUP BY position;
which gives me the output. there are only people who have vacations from #START to #END. If the person is not in the output, norm hours apply:
+----------+---------+--------+
| position | normhrs | wrkhrs |
+----------+---------+--------+
| 2.1 | 165 | 53 |
| 2.2 | 165 | 0 |
| 2.3 | 165 | 133 |
| 5.3.5.6 | 165 | 120 |
+----------+---------+--------+
4 rows in set, 6 warnings (0.00 sec)
This is the output i was looking for. i still need to come up with SELECT to sum up the actual working hours but it is easy enough not to put it in here.
Sorry, i know i'm over explaining some stuff but i being total noob, have found similar, rather superthorough answers, very helpful.

Select to Calculate Sales Average by Customer taking First Sales into Account MYSQL

I have this Sales Table by Customer in Mysql
+-----------+------------+-------+-----------------+
| Customer | Date | Sales | Date_First_Sale |
+-----------+------------+-------+-----------------+
| Jane | 2016-04-30 | 903 | 2015-02-03 |
| Jane | 2016-02-03 | 51 | 2015-02-03 |
| Jane | 2016-03-09 | 192 | 2015-02-03 |
| John | 2016-05-10 | 64 | 2015-10-03 |
| John | 2016-04-16 | 880 | 2015-10-03 |
| John | 2016-08-17 | 386 | 2015-10-03 |
| John | 2016-03-01 | 503 | 2015-10-03 |
| Juan | 2016-07-06 | 765 | 2015-09-01 |
| Juan | 2016-01-20 | 36 | 2015-09-01 |
| Juan | 2016-03-03 | 928 | 2015-09-01 |
| Momo | 2016-06-29 | 573 | 2015-09-01 |
| Momo | 2016-04-25 | 375 | 2015-09-01 |
| Momo | 2016-06-10 | 999 | 2015-09-01 |
| Nour | 2016-02-28 | 956 | 2015-05-01 |
| Nour | 2016-01-03 | 582 | 2015-05-01 |
| Nour | 2016-08-17 | 366 | 2015-05-01 |
| Philip | 2016-03-22 | 296 | 2015-09-01 |
| Philip | 2016-04-14 | 459 | 2015-09-01 |
| Sylvie | 2016-03-29 | 551 | 2015-09-03 |
| Sylvie | 2016-02-14 | 896 | 2015-09-03 |
+-----------+------------+-------+-----------------+
I need to calculate the Average Sales by Customer calculated on a WEEKLY basis in the last 12 months (52 or 53 weeks depending on the calendar?), starting from Today.
Now the problem is that I do not want to calculate the Average Weekly sales by customer for customers that have made their first purchase in a range below 12 months, for instance If current date is 2016-09-01, and Customers made his first purchase on 2016-07-24, the average should not be calculated on a 12 months basis but on the weekly sales generated between the 2016-07-24 and the 2016-09-01 only.
For customers who have made their First purchase before the 12 months range, then the average should be calculated on 12 months only.
I have been trying to find this SELECT but have not reached anything due to my limited Mysql knowledge for more complex queries!
Thanks in advance for your help
This should help you
SELECT Customer, (total_sales/weeks) AS avg_sales FROM
(
SELECT Customer, total_sales, Date_First_Sale, IF(weeks>52,52,weeks) as weeks
FROM (
SELECT Customer, SUM(Sales) AS total_sales, Date_First_Sale, TIMESTAMPDIFF(WEEK, Date_First_Sale, CURDATE()) AS weeks
FROM (
SELECT Customer, sales , Date_First_Sale
FROM test.SO_customer
WHERE Date > DATE_SUB(curdate(), INTERVAL 1 YEAR)
) as subTable
GROUP BY Customer
) as subTable2
) as subTable3

Maintain Revision History in MySQL Database

I am learning how PHP/jQuery/MySQL work with each other by creating a web app that maintains a record of my bills and payment made.
I can't wrap my head around on how to structure my database so that I can keep track of payments made. Kinda like a payment history.
I would like to maintain a history of payment amount and the date the payment was made.
Here is my current 'Bills' table.
+----+-------------+-------------+-----------------+----------+
| id | creditor | amount_owed | monthly_payment | due_date |
+----+-------------+-------------+-----------------+----------+
| 42 | Capital One | 500.00 | 55.00 | 21 |
| 43 | sdf | 33.00 | 33.00 | 30 |
| 44 | Car Loan | 15000.00 | 300.00 | 14 |
+----+-------------+-------------+-----------------+----------+
I was thinking about adding a couple of columns (payment amount/payment date) and duplicating the ids... Anybody of any suggestions?
Thanks!
I would create a new table bills_payments with an FK to the bills table. That way you can keep track of each paymant made for every bill registry. And plus, you can put interest fees/charges on this table to. It will be like:
Bills
+----+-------------+-------------+-----------------+----------+
| id | creditor | amount_owed | monthly_payment | due_date |
+----+-------------+-------------+-----------------+----------+
| 42 | Capital One | 500.00 | 55.00 | 21 |
| 43 | sdf | 33.00 | 33.00 | 30 |
| 44 | Car Loan | 15000.00 | 300.00 | 14 |
+----+-------------+-------------+-----------------+----------+
Bills_payments
+----+-------------+-------------+-----------------+---------------+
| idBill | paydValue | date | InterestFee | due_date_limit|
+--------+------------------------+----------------+---------------+
| 42 | 55.00 | 2013-11-21 | | 2013-11-21 |
| 43 | 45.00 | 2013-12-05 | 12.00 | 2013-11-30 |
| 44 | 300.00 | 2013-12-14 | 300.00 | 2013-12-14 |
+----+-------------+-------------+-----------------+---------------+
The due date limit you will have to calculate since you just put the due date on the main table.

Magento Join two tables

I am new in magento.
I am creating a custom megento report. I decide to join two tables rev_table and emp_table.
emp_id | datefield | emp_name | salary |cpf | other_benefits
1 | 2013-11-12 00:00:00 | abc | 18000 | 2000 | 3000
2 | 2013-11-20 00:00:00 | test1 | 20000 | 1000 | 1000
3 | 2013-11-21 00:00:00 | test | 18000 | 2000 | 2000
4 | 2013-12-11 00:00:00 | demo | 15000 | 3000 | 2000
5 | 2013-12-20 00:00:00 | ash | 8000 | 5000 | 3000
6 | 2013-12-24 00:00:00 | test1 | 18000 | 1000 | 500
revenue_id | datefield | revenue | rental | repair | recruitment |wages
1 | 2013-12-24 07:02:00 | 200 | 22 | 23 | 546 |
2 | 2013-11-19 01:01:00 | 456 | 56 | 56 | 565 |
3 | 2013-10-09 00:00:00 | 500 | 565 | 564 | 56 |
4 | 2013-11-13 00:00:00 | 900 | 435 | 345 | 5 |
i want to show total of employee table of 1 month into rev_table report in magento. for example employes total wages of november month is (67000). this will show as a new column in revenue report of november month.
then result should be
revenue_id | datefield | revenue | rental | repair | recruitment | wages <br/>
2 | 2013-11-19 01:01:00 | 456 | 56 | 56 | 565 | 67000 <br/>
4 | 2013-11-13 00:00:00 | 900 | 435 | 345 | 5 |
please help me to explain me how to add joins in magento collections and add coloume in report grid.
Thanks