I have a table which is recording utilization of a product.
In logistics, there's a parameter called commodity average monthly utilization (CAMU) this keep track of your monthly utilization to date.
here's my table tblTransaction
desired output would be this
qry_camu
Explanation:
prod1 has a total of 102 issuance recorded in 2 months (Feb and Sep) - 102/2 = 51
prod2 has a total of 26 issuance recorded in 2 months (Apr and May) - 26/2 = 13
Thank you in advance!
Assuming the table is created with the standard date format for the column date.
So the query will be as below
select
commodity,
sum(issuance)/count(distinct extract(year_month from date)) as CAMU,
round(sum(issuance)/count(distinct extract(year_month from date)),2) as 'CAMU(rounded)'
from
tbltransaction
group by
commodity;
Output:
commodity | CAMU | CAMU(rounded)
:-------- | ------: | ------------:
prod1 | 51.0000 | 51.00
prod2 | 13.0000 | 13.00
db<>fiddle here
Related
i have a normal turnover table with 3 columns "customer" , "year", "amount"
(for example)
customer
year
amount
anton
2020
$5
paul
2019
$12
anton
2021
$5
paul
2019
$10
felicia
2021
$5
anton
2019
$12
felipe
2019
$12
and i have the following mysql query
SELECT `customer` , SUM(`amount`) as summ FROM `customer`.`accountsales` WHERE `amount`> 0 GROUP BY `customer` ORDER BY summ DESC ;
This transaction gives me a nice Paretto table with the sales of each customer in descending order
name
sales all years
customer1
sum of all transactions of customer1
customer2
sum of all transactions of customer2
customer3
sum of all transactions of customer3
so far so good, i want to go one step further and i want to create the following table
Name
Sales all years
Sales 2021
Sales2020
Sales2019
customer1
sum1
sum2021 from customer1
sum2020 from customer1
sum2019 from customer1
customer2
sum2
sum2021 from customer2
sum2020 from customer2
sum2019 from customer2
customer3
sum3
sum2021 from customer3
sum2020 from customer3
sum2019 from customer3
but i want to do it in only one transaction, because the initial table is very huge.
can someone give a hint ?
p.S. feel free to edit the title since I am not very inspired today
You can try to use condition aggregate function
Query 1:
SELECT `customer` ,
SUM(`amount`) 'Sales all years',
SUM(CASE WHEN year = 2021 THEN `amount` ELSE 0 END) Sales2021,
SUM(CASE WHEN year = 2020 THEN `amount` ELSE 0 END) Sales2020,
SUM(CASE WHEN year = 2019 THEN `amount` ELSE 0 END) Sales2019
FROM accountsales
GROUP BY `customer`
Results:
| customer | Sales all years | Sales2021 | Sales2020 | Sales2019 |
|----------|-----------------|-----------|-----------|-----------|
| anton | 22 | 5 | 5 | 12 |
| felicia | 5 | 5 | 0 | 0 |
| felipe | 12 | 0 | 0 | 12 |
| paul | 22 | 0 | 0 | 22 |
I have this query to extract total_hours, start_date and end_date:
select proj.start_date, proj.end_date, sum(ifnull(work.hours_estimate,0)) as total_hours
from project_table proj
left outer join project_task work on
work.project_id = proj.id
where proj.id = 3
This query gives me a single row of result:
start_date | end_date | total_hour
----------------------------------------
2017-04-24 | 2017-05-15 | 119
What I want is to generate a daily interval of rows, constantly decreasing the total_hours by a certain amount, say 19 hours, and the day increasing by 1 day.
Expected results:
day | hours_left
------------------------
2017-04-24 | 119
2017-04-25 | 100
2017-04-26 | 81
2017-04-27 | 62
2017-04-28 | 43
2017-04-29 | 24
... and so on and so forth until it reaches 2017-05-15 (of course, no negative for hours_left, just zero if negative)
can't seem to figure out how to do this.
QUESTIONS:
1.) Is this possible in MySQL?
2.) If this is possible in MySQL, is it efficient/convinient?
If not, I could just do it in application, as state in the comments
There is a query I am trying to implement in which I am not having much success with in trying to find the MAX and MIN for each week.
I have 2 Tables:
SYMBOL_DATA (contains open,high,low,close, and volume)
WEEKLY_LOOKUP (contains a list of weeks(no weekends) with a WEEK_START and WEEK_END)
**SYMBOL_DATA Example:**
OPEN, HIGH, LOW, CLOSE, VOLUME
23.22 26.99 21.45 22.49 34324995
WEEKLY_LOOKUP (contains a list of weeks(no weekends) with a WEEK_START and WEEK_END)
**WEEKLY_LOOKUP Example:**
WEEK_START WEEK_END
2016-01-25 2016-01-29
2016-01-18 2016-01-22
2016-01-11 2016-01-15
2016-01-04 2016-01-08
I am trying to find for each WEEK_START and WEEK_END the high and low for that particular week.
For instance, if the WEEK is WEEK_START=2016-01-11 and WEEK_END=2016-01-15, I would have
5 entries for that particular symbol listed:
DATE HIGH LOW
2016-01-15 96.38 93.54
2016-01-14 98.87 92.45
2016-01-13 100.50 95.21
2016-01-12 99.96 97.55
2016-01-11 98.60 95.39
2016-01-08 100.50 97.03
2016-01-07 101.43 97.30
2016-01-06 103.77 100.90
2016-01-05 103.71 101.67
2016-01-04 102.24 99.76
For each week_ending (2016-01-15) the HIGH is 100.50 on 2016-01-13 and the LOW is 92.45 on 2016-01-14
I attempted to write a query that gives me a list of highs and lows, but when I tried adding a MAX(HIGH), I had only 1 row returned back.
I tried a few more things in which I couldn't get the query to work (some sort of infinite run type). For now, I just have this that gives me a list of highs and lows for every day instead of the roll-up for each week which I am not sure how to do.
select date, t1.high, t1.low
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='ABCDE' and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
and t1.date<=CURDATE()
LIMIT 30;
How can I get for each week (Start and End) the High_Date, MAX(High), and Low_Date, MIN(LOW) found each week? I probably don't need a
full history for a symbol, so a LIMIT of like 30 or (30 week periods) would be sufficient so I can see trending.
If I wanted to know for example each week MAX(High) and MIN(LOW) start week ending 2016-01-15 the result would show
**Result:**
WEEK_ENDING 2016-01-15 100.50 2016-01-13 92.45 2016-01-14
WEEK_ENDING 2016-01-08 103.77 2016-01-06 97.03 2016-01-08
etc
etc
Thanks to all of you with the expertise and knowledge. I greatly appreciate your help very much.
Edit
Once the Week Ending list is returned containing the MAX(HIGH) and MIN(LOW) for each week, is it possible then on how to find the MAX(HIGH) and MIN(LOW) from that result set so it return then only 1 entry from the 30 week periods?
Thank you!
To Piotr
select part1.end_date,part1.min_l,part1.max_h, s1.date, part1.min_l,s2.date from
(
select t2.start_date, t2.end_date, max(t1.high) max_h, min(t1.low) min_l
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='FB'
and t1.date<='2016-01-22'
and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
group by t2.start_date, t2.end_date order by t1.date DESC LIMIT 1;
) part1, symbol_data s1, symbol_data s2
where part1.max_h = s1.high and part1.min_l = s2.low;
You will notice that the MAX and MIN for each week is staying roughly the same and not changing as it should be different for week to week for both the High and Low.
SQL Fiddle
I have abbreviated some of your names in my example.
Getting the high and low for each week is pretty simple; you just have to use GROUP BY:
SELECT s1.symbol, w.week_end, MAX(s1.high) AS weekly_high, MIN(s1.LOW) as weekly_low
FROM weeks AS w
INNER JOIN symdata AS s1 ON s1.zdate BETWEEN w.week_start AND w.week_end
GROUP BY s1.symbol, w.week_end
Results:
| symbol | week_end | weekly_high | weekly_low |
|--------|---------------------------|-------------|------------|
| ABCD | January, 08 2016 00:00:00 | 103.77 | 97.03 |
| ABCD | January, 15 2016 00:00:00 | 100.5 | 92.45 |
Unfortunately, getting the dates of the high and low requires that you re-join to the symbol_data table, based on the symbol, week and values. And even that doesn't do the job; you have to account for the possibility that there might be two days where the same high (or low) was achieved, and decide which one to choose. I arbitrarily chose the first occurrence in the week of the high and low. So to get that second level of choice, you need another GROUP BY. The whole thing winds up looking like this:
SELECT wl.symbol, wl.week_end, wl.weekly_high, MIN(hd.zdate) as high_date, wl.weekly_low, MIN(ld.zdate) as low_date
FROM (
SELECT s1.symbol, w.week_start, w.week_end, MAX(s1.high) AS weekly_high, MIN(s1.low) as weekly_low
FROM weeks AS w
INNER JOIN symdata AS s1 ON s1.zdate BETWEEN w.week_start AND w.week_end
GROUP BY s1.symbol, w.week_end) AS wl
INNER JOIN symdata AS hd
ON hd.zdate BETWEEN wl.week_start AND wl.week_end
AND hd.symbol = wl.symbol
AND hd.high = wl.weekly_high
INNER JOIN symdata AS ld
ON ld.zdate BETWEEN wl.week_start AND wl.week_end
AND ld.symbol = wl.symbol
AND ld.low = wl.weekly_low
GROUP BY wl.symbol, wl.week_start, wl.week_end, wl.weekly_high, wl.weekly_low
Results:
| symbol | week_end | weekly_high | high_date | weekly_low | low_date |
|--------|---------------------------|-------------|---------------------------|------------|---------------------------|
| ABCD | January, 08 2016 00:00:00 | 103.77 | January, 06 2016 00:00:00 | 97.03 | January, 08 2016 00:00:00 |
| ABCD | January, 15 2016 00:00:00 | 100.5 | January, 13 2016 00:00:00 | 92.45 | January, 14 2016 00:00:00 |
To get the global highs and lows, just remove the weekly table from the original query:
SELECT wl.symbol, wl.high, MIN(hd.zdate) as high_date, wl.low, MIN(ld.zdate) as low_date
FROM (
SELECT s1.symbol, MAX(s1.high) AS high, MIN(s1.low) as low
FROM symdata AS s1
GROUP BY s1.symbol) AS wl
INNER JOIN symdata AS hd
ON hd.symbol = wl.symbol
AND hd.high = wl.high
INNER JOIN symdata AS ld
ON ld.symbol = wl.symbol
AND ld.low = wl.low
GROUP BY wl.symbol, wl.high, wl.low
Results:
| symbol | high | high_date | low | low_date |
|--------|--------|---------------------------|-------|---------------------------|
| ABCD | 103.77 | January, 06 2016 00:00:00 | 92.45 | January, 14 2016 00:00:00 |
The week table seems entirely redundant...
SELECT symbol
, WEEK(zdate)
, MIN(low) min
, MAX(high) max_high
FROM symdata
GROUP
BY symbol, WEEK(zdate);
This is a simplified example. In reality, you might use DATE_FORMAT or something like that instead.
http://sqlfiddle.com/#!9/c247f/3
Check if following query produces desired result:
select part1.end_date,part1.min_l,part1.max_h, s1.date, part1.min_l,s2.date from
(
select t2.start_date, t2.end_date, max(t1.high) max_h, min(t1.low) min_l
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='ABCDE'
and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
group by t2.start_date, t2.end_date
) part1, symbol_data s1, symbol_data s2
where part1.max_h = s1.high and part1.min_l = s2.low
and (s1.date >= part1.start_date and part1.end_date)
and (s2.date >= part1.start_date and part1.end_date)
I am creating a web application based on attendance system. I am using MySQL and PHP. I have a table for every employee that has took attendance and they keep adding when the employees takes attendance on the machine. I extracted the employees that has has attendance 2 times per day which are in and out, and I added them to a table with columns. Now I want to extract the attendances for the employees that has took attendance 1 per day or more than 2 per day which they are error and i want to add them to a table employee time1, time2 and time3.
I have a table as this design, it's an attendance system,
I need to split the time if the attendance count per day was not equal to 2, which are error logs.
id pd time date
1 5 07:05 08/07/2014
2 4 18:02 07/07/2014
3 1 07:05 06/07/2014
4 1 07:06 06/07/2014
5 1 18:00 06/07/2014
I need to add them to a table in the database and to be split in that form with respect the pd and date.
id pd time1 time2 time3 .... date
1 5 07:05 08/07/2014
2 4 18:02 07/07/2014
3 1 07:05 07:06 18:00 06/07/2014
sorry guys its my first post in my whole entire life , so forgive me if am doing something wrong or not complete
The following result, which isn't an exact match to your request, but it may get you started. It doesn't attempt to list all the starting times, but does count the number of entries per pd per date so any that are not equal to 2 can be detected easily.
| ID | PD | MIN_TIME | COUNT_TIME | MAX_TIME | DATE |
|----|----|----------|------------|----------|-------------------------------|
| 3 | 1 | 07:05 | 3 | 18:00 | June, 07 2014 00:00:00+0000 |
| 2 | 4 | 18:02 | 1 | 18:02 | July, 07 2014 00:00:00+0000 |
| 1 | 5 | 07:05 | 1 | 07:05 | August, 07 2014 00:00:00+0000 |
The query for this is:
SELECT
MIN(id) AS id
, pd
, MIN(time) AS min_time
, COUNT(time) AS count_time
, MAX(time) AS max_time
, date
FROM AttendanceTbl
GROUP BY
pd
, date
ORDER BY
pd
, date
You can review it at this SQLFIDDLE
Suggestions.
include table name(s) as well as the sample data.
consider using http://sqlfiddle.com to provide a working example for development of the solution
Note:
- I really don't like using date or time as field names, I know MySQL lets you do it but I can't recommend it.
Here is the solution thank alot for all :)
$sql="SELECT MIN(id) AS id,date, enrollnumber,
MIN((CASE WHEN ordinal=1 THEN time END)) AS time1,
MIN((CASE WHEN ordinal=2 THEN time END)) AS time2,
MIN((CASE WHEN ordinal=3 THEN time END)) AS time3,
MIN((CASE WHEN ordinal=4 THEN time END)) AS time4,
MIN((CASE WHEN ordinal=5 THEN time END)) AS time5,
MIN((CASE WHEN ordinal=6 THEN time END)) AS time6,
MIN((CASE WHEN ordinal=7 THEN time END)) AS time7,
MIN((CASE WHEN ordinal=8 THEN time END)) AS time8,
MIN((CASE WHEN ordinal=9 THEN time END)) AS time9,
MIN((CASE WHEN ordinal=10 THEN time END)) AS time10
FROM (
SELECT t.id, t.enrollnumber, t.time, t.date,
(SELECT 1+COUNT(*)
FROM attlog AS sub
WHERE sub.enrollnumber=t.enrollnumber AND
sub.date=t.date AND (
sub.time<t.time OR
sub.time=t.time AND sub.id<t.id)) AS ordinal
FROM attlog AS t
) AS x
GROUP BY enrollnumber, date";
I'm trying to make a listing for Ageing Transaction where areas the list will have 0-30 days column and 31-60 days column.
If transaction date is within 30 days, then its in 0-30 days column, if it's within the range of 31-60 then it's inside 31-60 days column.
So to make it more clear, here's the table
|trans_code |customer |trans_date | credit| debit |
|ABC1000 |John ptd |2014-05-20 | 0.00 | 200.00 |
|ABC1000 |John ptd |2014-07-06 |200.00 | 0.00 |
|ABC1001 |Petron |2014-04-25 | 0.00 | 600.00 |
|ABC1001 |Petron |2014-06-10 |600.00 | 0.00 |
John ptd has a debt of $200 on 2014-05-20 and he paid his debt on 2014-07-06. So the date range is within 31-60 days. The $200 he paid will goes into 31-60 days column in Ageing Transaction listing(like below).
|Customer | 0-30 days | 31-60 days|
|John ptd | 0.00 | 200.00|
|Petron | 0.00 | 600.00|
Now my problem is how to compare trans_date since it's in the same column using SQL. It should be BASED on trans_code.
Updated: It's actually starts with Debit before Credit
Here is a better approach:
SELECT
d.customer,
CASE WHEN DATEDIFF(c.trans_date, d.trans_date) <= 30 THEN d.debit ELSE 0.0 END AS `0-30`,
CASE WHEN DATEDIFF(c.trans_date, d.trans_date) > 30 AND DATEDIFF(c.trans_date, d.trans_date) <= 60
THEN d.debit ELSE 0.0 END AS `31-60`
FROM table AS d
INNER JOIN table AS c ON c.trans_code = d.trans_code and c.customer = d.customer
WHERE d.credit = 0.0 and c.debit = 0.0
SELECT
t.customer,
SUM(c1.credit) AS `0-30`,
SUM(c2.credit) AS `31-60`,
FROM table AS t
LEFT JOIN table AS c1 ON c1.debit = t.credit AND DATEDIFF(t.trans_date, c1.trans_date) <= 30
LEFT JOIN table AS c2 ON c2.debit = c2.credit AND DATEDIFF(t.trans_date, c2.trans_date) > 30 DATEDIFF(t.trans_date, c2.trans_date) <= 60
Take a look at the DATEDIFF function. You JOIN to the transactions paid within 30 days and to the ones paid within 31-60 days.
You can use GROUP_CONCAT.It will look like
SELECT trans_code ,customer, GROUP_CONCAT(trans_date), credit, debit, 0-30 days
, 31-60 days
FROM table1 INNER JOIN table2 ON table1.customer = table2.customer
WHERE customer = "John ptd" GROUP BY trans_code
Not TESTED.