How to have a conditional SEPARATOR in a GROUP_CONCAT MySQL select - php

I don't know if this can be done or not. In my MySQL code one of my selects is:
GROUP_CONCAT(timestamp SEPARATOR '~') AS times
What I'd like to do is test the timestamps for being equal at the date/hour/min level and if it is replace the SEPARATOR with a <br>
instead of the tilde. Is there a way to do this in MySQL? If not how would I go about doing it in PHP? The end result needs to be the two (or more) timestamps making a string, which will be used in a table cell. If the date/hour/min are equal then both would be in the same cell. If it helps the timestamps would almost certainly be sequential in the table.

You would typically do that with two levels of aggregation. First aggregate by minute and group_concat with the <br> separator, then aggregate the resulting string again with the ~ separator.
Assuming a table called mytable with a timestamp column called mytimestamp, you would do:
select group_concat(gc0 order by myminute separator '~') gc1
from (
select
date_format(mytimestamp, '%Y-%m-%d %H:%i') myminute,
group_concat(mytimestamp order by mytimestamp separator '<br>') gc0
from mytable
group by myminute
) t
Demo on DB Fiddle:
Sample data:
| mytimestamp |
| :------------------ |
| 2019-01-01 00:00:00 |
| 2019-01-01 00:00:01 |
| 2019-01-01 00:00:10 |
| 2019-01-01 00:01:00 |
| 2019-01-02 14:10:00 |
| 2019-01-02 14:10:30 |
Query results:
| gc1 |
| :------------------------------------------------------------------------------------------------------------------------------- |
| 2019-01-01 00:00:00<br>2019-01-01 00:00:01<br>2019-01-01 00:00:10~2019-01-01 00:01:00~2019-01-02 14:10:00<br>2019-01-02 14:10:30 |

Related

How to use Group By, but keep all other values in the column (in mysqli)

Is it possible to use Group By, yet keep all of the other associated values from the columns ?
month | date | location
---------------------------
april | 4/11/18 | US
april | 4/16/18 | US
may | 5/14/18 | Canada
june | 6/05/18 | Canada
june | 6/30/18 | US
Basically, I want to show distinct values for the month, but match the date underneath the respective month, and keep the other values.
april: 4/11/18 (US), 4/16/18 (US)
may: 5/14/18 (Canada)
june: 6/05/18 (Canada), 6/30/18 (US)
Here is the code I used:
$query = ("SELECT * FROM events GROUP BY month");
This is just showing the distinct months.
Any help would be greatly appreciated.
Use GROUP_CONCAT
SQL DEMO
SELECT month,
GROUP_CONCAT( date, '(', location, ')' ) as data
FROM YourTable
GROUP BY month
OUTPUT
| month | data |
|-------|-----------------------------------------------------|
| april | 2018-04-11 00:00:00(US),2018-04-16 00:00:00(US) |
| june | 2018-06-05 00:00:00(Canada),2018-06-30 00:00:00(US) |
| may | 2018-05-14 00:00:00(Canada) |

MySql AVG() and WHERE

I'm struggling on how to write this query and cant quite find an answer to help me with my case.
Consider the following table:
-----------------------------------------------
| ID | Value1 | Value2 | Value3 | Date |
-----------------------------------------------
| 1 | 10 | 23 | 30 | 2015-01-01 |
-----------------------------------------------
| 1 | 11 | 33 | 40 | 2015-02-01 |
-----------------------------------------------
| 2 | 26 | 93 | 20 | 2015-01-01 |
-----------------------------------------------
| 2 | 11 | 33 | 50 | 2015-02-01 |
-----------------------------------------------
I want to retrieve the average value of Value1 where the Date is 2015-01-01
I thought that
SELECT AVG(PAM_1) FROM MyTable WHERE DATE = 2015-01-01
would work but of course it does not. I'm aware that I probably need to use HAVING but I'm being confused if I must also use GROUP BY and if do I need the AS (something) part.
EDIT
The problem was not related to the query. I was supplying the date trough a variable as such:
$sth = $db->prepare("SELECT AVG(Value1) FROM MyTable WHERE DATE = $date");
Which is not possible to do with prepared statements.
Your query is basically fine. Your date constant is not. Dates constants should be enclosed in single quotes:
SELECT AVG(PAM_1)
FROM MyTable
WHERE DATE = '2015-01-01';
If the date could have a time component, then the following is the best way to handle this:
SELECT AVG(PAM_1)
FROM MyTable
WHERE DATE >= '2015-01-01' AND DATE < '2015-01-02';

SQL PHP Retrieve total money for a specific Store

Is there a query that retrieve the TotalMoney,TotalPaidMoney,TotalReturnMoney for each StoreName.
Like:
StoreName| TotalPaid | ToatalReturn | TotalMoney
Pizza | sum of PaidMoney | sum of ReturnMoney | sum of TotalMoney
|
From this Table
Date | StoreName|PaidMoney| ReturnMoney| TotalMoney
*********
2014 | Pizaa | 33$ | 12$ | 120$
2014 Main 12$ 23$ 123$
2014 | Pizaa | 33$ | 12$ | 120$
2014 | Main | 33$ | 12$ | 120$
Table I Have:
Store Table
You can easily use a GROUP BY statement, like so :
SELECT StoreName, SUM(PaidMoney) as TotalPaid, SUM(ReturnMoney) as TotalReturn, SUM(TotalMoney) as TotalMoney FROM store_table GROUP BY StoreName
You can use the following command:
SELECT SUM(PaidMoney) AS TotalPaid, SUM(ReturnMoney) AS TotalReturn, SUM(TotalMoney) AS TotalMoney FROM Table WHERE Store='Pizaa';
Or if you want all stores and regroup by store:
SELECT SUM(PaidMoney) AS TotalPaid, SUM(ReturnMoney) AS TotalReturn, SUM(TotalMoney) AS TotalMoney FROM Table GROUP BY Store;
If you really have this currency signs in your table, it might be a bit more complicated. I now assume, that you always have the $ sign as last sign in your column and that there are no decimal values:
SELECT
StoreName,
SUM(PAID_MONEY_VALUE) AS SUM_PAID_MONEY_VALUE,
SUM(RETURN_MONEY_VALUE) AS SUM_RETURN_MONEY_VALUE,
SUM(TOTAL_MONEY_VALUE) AS SUM_TOTAL_MONEY_VALUE
FROM
(
SELECT
StoreName,
CONVERT(int, SUBSTRING(PaidMoney, 1, LEN(PaidMoney - 1))) AS PAID_MONEY_VALUE,
CONVERT(int, SUBSTRING(ReturnMoney, 1, LEN(ReturnMoney - 1))) AS RETURN_MONEY_VALUE,
CONVERT(int, SUBSTRING(TotalMoney, 1, LEN(TotalMoney - 1))) AS TOTAL_MONEY_VALUE
FROM <YOUR_TABLE>
) AS SUBQUERY
GROUP BY StoreName

PHP MySQL SUM Between Month

I want to use BETWEEN in my PHP - MySQL.
Example I have table :
badge_id | balance_type | balance_amount | balance_month
110702 | aa | 250000 | January-2013
110702 | ab | 100000 | January-2013
110702 | aa | 100000 | February-2013
110702 | ab | 90000 | February-2013
110702 | aa | 100000 | March-2013
110702 | ab | 110000 | March-2013
Now I want to SUM just for Balance Month from January-2013 until February-2013.
SELECT balance_type AS balance_type, SUM(balance_amount) AS value_sum
FROM t_balance
WHERE badge_id = '110702' && balance_month BETWEEN 'August-2013'
and 'September-2013'
GROUP BY balance_type
But it will SUM all balance_month.
Any advice?
It's because you have a string column for balance_month. BETWEEN August-2013 and September-2013 is basically BETWEEN A AND S in the alphabet (don't know how to describe it better, lacking english skills here). J for January and F for February is between A and S, so MySQL counts those in. Convert your column to a date
WHERE ... AND STR_TO_DATE(balance_month, '%M-%Y') BETWEEN '2013-08-01'
and '2013-09-30'
But, if I were you I'd get rid of the column and use a real date column. Otherwise an index can not be used on it, when you always have to use a function like str_to_date() on it.
This will work...
SELECT balance_type AS balance_type, SUM(balance_amount) AS value_sum
FROM t_balance
WHERE badge_id = '110702' && STR_TO_DATE(balance_month,'%M-%Y') BETWEEN '2013-08-01'
and '2013-09-30'
GROUP BY balance_type
By an unfortunate twist of fate, all months are between August-2013 and September-2013 -- as you are probably comparing on strings not date.
On MySQL, proper date have the DATE (or one derivable) type and should be formatted as YYYY-MM-DD.

MySQL insert into select data on duplicate key update using correlated sub queries with calculated field

1.emp_ot
table use to define maximum amount of ot hours which employee can do
within specific period
2.daily_attend describe what is actually happen
3.my tables shortened form as follows
mysql> select punchDate,empNO,ot from daily_attend;
+------------+--------+----------+
| punchDate | empNO | ot |
+------------+--------+----------+
| 2012-02-20 | 000123 | 02:00:00 |
| 2012-02-02 | 000123 | 01:00:00 |
| 2012-02-01 | 000126 | 01:00:00 |
| 2012-02-01 | 000123 | 01:00:00 |
+------------+--------+----------+
4 rows in set (0.01 sec)
mysql> select permitId,permitMonth,empNo,dayFrom,dayTO,permitOt from
emp_ot;
+----------+-------------+--------+------------+------------+----------+
| permitId | permitMonth | empNo | dayFrom | dayTO | permitOt |
+----------+-------------+--------+------------+------------+----------+
| 1 | 02 | 000123 | 2012-02-01 | 2012-02-10 | 02:00:00 |
| 2 | 02 | 000123 | 2012-02-20 | 2012-02-25 | 03:00:00 |
| 3 | 02 | 000126 | 2012-02-01 | 2012-01-10 | 02:00:00 |
| 4 | 03 | 000123 | 2012-03-01 | 2012-03-10 | 05:00:00 |
+----------+-------------+--------+------------+------------+----------+
4 rows in set (0.00 sec)
4.here i want to update emp_ot.workedOt to know how many ot hours
had worked under each permitId by employee
ex:
+----------+------------+----------+----------+
| permitId | empNo | workedOt | permitOt |
+----------+----------+----------+----------+
| 1 | 000123 | 02:00:00 | 02:00:00 |
| 2 | 000123 | 02:00:00 | 03:00:00 |
| 3 | 000126 | 01:00:00 | 02:00:00 |
| 4 | 000123 | 00:00:00 | 05:00:00 |
+----------+------------+----------+----------+
5.i'm using mysql event which driven at every day at 00:00:01 that
because table should be automatically update without messing old data
6.my try as follows:
DROP EVENT IF EXISTS make_emp_ot;
DELIMITER |
CREATE EVENT make_emp_ot
ON SCHEDULE EVERY 1 DAY STARTS
TIMESTAMP(CURRENT_DATE,'00:00:01')
DO
BEGIN
INSERT INTO emp_ot( permitMonth,empNo, dayFrom, dayTo, workedOt)
SELECT o.permitId,EXTRACT(MONTH FROM CURRENT_DATE)AS
permitMonth ,o.empNo,'','',(
SELECT ifnull( SEC_TO_TIME(SUM(TIME_TO_SEC(d.ot)))
,'00:00:00')
FROM daily_attend d
WHERE d.punchDate BETWEEN o.dayFrom AND o.dayTo
GROUP BY o.empNo)AS ot
FROM emp_ot o
LEFT JOIN daily_attend d on o.empNo=d.empNo
WHERE o.permitMonth=EXTRACT(MONTH FROM CURRENT_DATE)
ON DUPLICATE KEY UPDATE
workedOt=VALUES(workedOt) ;
END; |
DELIMITER ;
7.i suspect there were bad table joining and actually i don't want
insert part, how to use only update part?
8.please help me to correct these things.Thank you every one.
i'm using mysql event which driven at every day at 00:00:01 that because table should be automatically update without messing old data
Have you considered using triggers?
i suspect there were bad table joining
There are a number of problems with your INSERT ... ON DUPLICATE KEY UPDATE statement:
You specify only five columns in the INSERT part, but the SELECT returns six (I suspect you did not intend to SELECT permitId;
The empty string '' is not a valid date literal for inserting into the dayFrom and dayTo columns (assuming that they are of MySQL's DATE type);
The GROUP BY within your subquery does not make much sense, as the outer query expects only one record: you should restrict its recordset to matches on empNo and remove its GROUP BY clause (all rows will be implicitly grouped by the aggregate function);
Since you have the correlated subquery, joining the tables in the outer query is unnecessary and erroneous (however you could achieve the same results without the correlated subquery by using such a join and then grouping the outer query, which would be more performant);
It isn't clear whether you have appropriate UNIQUE keys defined in order for the ON DUPLICATE KEY UPDATE part to function as expected.
and actually i don't want insert part, how to use only update part?
Use UPDATE instead of INSERT:
UPDATE emp_ot e
SET permitOt := (
SELECT IFNULL(SEC_TO_TIME(SUM(TIME_TO_SEC(d.ot))),0)
FROM daily_attend d
WHERE d.empNo = e.empNo
AND d.punchDate BETWEEN e.dayFrom AND e.dayTO
)
WHERE permitMonth = EXTRACT(MONTH FROM CURRENT_DATE)
See it on sqlfiddle.

Categories