I have a problem with a MySQL event. I am making a gym membership manager with java and using MySQL as database. There are three tables which I need to use for this event as shown below:
------------------------- --------------------- --------------------------
| Daily Statements | | Monthly Statements | | Daily Total Statements |
------------------------- --------------------- --------------------------
| ID (PK) | | ID (PK) | | Ref ID (PK) |
| Value Paid | | Value Paid | | Total Daily Value |
| Date Paid | | Date Paid | | Date |
------------------------- ----------------------- ---------------------------
So the event has to sum up every day bills and make a summary of each day or differently said has to make a daily total. But I need it to summarize both daily memberships and monthly ones into one row with the current timestamp. I have thought about using a query as follows:
INSERT INTO `daily_total_statements`(
`Reference ID`,
`Value`,
`Date`
) VALUES (
Null,
(
SELECT
SUM(`Value Paid`)
FROM `daily_statements`
WHERE `Date Paid` = CURRENT_DATE()),
CURRENT_DATE()
)
It works for one table but I can't figure out how to do it for two tables. I think it has to be done with conditionals but I'm not good at that since I'm a beginner. Should I create two distinct events or a trigger instead?
P.s. I want to use the table for creating a line graph with JFreeGraph. Sorry for bad lang. :)
Use a UNION of the two tables, and then combine their totals with SUM().
INSERT INTO daily_total_statements (`Reference ID`, `Value`, `Date`)
SELECT NULL, SUM(combined.total), CURRENT_DATE()
FROM (
SELECT SUM(`Value Paid`) AS total
FROM daily_statements
WHERE `Date Paid` = CURRENT_DATE()
UNION ALL
SELECT SUM(`Value Paid`) AS total
FROM monthly_statements
WHERE `Date Paid` = CURRENT_DATE()
) AS combined
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 10 months ago.
Improve this question
I need to store tariffs connected to a port.
So, the table can look like this:
create table tariffs(
int NOT NULL AUTO_INCREMENT,
price decimal(12,2),
expiry bigint(11)
)
expiry represents a timestamp when that particular tariff will expire.
So I might have data like this:
id | price | expiry
1 | 11.00 | 30/Jan/2022
2 | 12.00 | 30/Feb/2022
3 | 13.00 | 30/Mar/2022
4 | 14.00 | 30/Apr/2022
5 | 15.00 | null
In this case, ID 5 isn't expired yet, meaning that it's current.
(I realise I put dates, there, rather than timestamps; I did so that it's easier to read)
The problem I have is in the logic to figure out which tariff to use given a specific date.
In an ideal world, if 5 were "Infinite", I could just do WHERE expiry > date_apply limit 1 -- however, I don't have that luxury since date_apply won't be returned at all.
I COULD assign a very big number to expiry for the "current" entry. It would make the query work regardless. But... it feels wrong.
Somebody recommended using TWO fields for each tariff, a "from" and a "to", telling me that otherwise querying will be a nightmare. I am beginning to see what they mean... but then I fear operators might unwillingly have "holes" in the timeframes for tariffs, which would be difficult to prevent.
How should I organise my table, and how should I query it? What's the best practices here?
SELECT COALESCE(t2.price, t1.price) AS price
FROM (SELECT price FROM tariffs WHERE expiry IS NULL LIMIT 1) AS t1
LEFT OUTER JOIN (SELECT price FROM tariffs WHERE expiry > ? ORDER BY expiry DESC LIMIT 1) AS t2
Demo: https://www.db-fiddle.com/f/wykqR5X7B9S424AWkA4aQy/1
The first subquery is bound to return 1 row if you have at least one unexpired tariff.
The second subquery may not return 1 row, if you put in a date too late. So I change this join to LEFT OUTER JOIN. If there is no matching row for the condition on expiry, the subquery will return no rows, and the outer join will replace these with NULLs.
So if t2.* is NULL, then the COALESCE() defaults to the unexpired value in t1.price.
You can leave the ultimate price with an expiry null and we can use coalesce to assign a value according to the logic needed at the time.
Here we start with only an expired tarif and the expiry = null tarif. We create a view that gives us the expiry as undefined. We then add a tarif which is valid and it is correctly returned by the same view.
create table tariffs(
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
price decimal(12,2),
expiry date);
insert into tariffs (price,expiry) values (11,'2022-01-30'),(12,null);
create view current_tarif as
select id, price, coalesce(expiry,'undefined') expiry
from tariffs
where coalesce(expiry,'3000-12-31') > curdate()
order by coalesce(expiry,'3000-12-31')
limit 1;
select * from current_tarif;
id | price | expiry
-: | ----: | :--------
2 | 12.00 | undefined
insert into tariffs (price,expiry) values (15,'2022-12-30');
select * from current_tarif;
id | price | expiry
-: | ----: | :---------
3 | 15.00 | 2022-12-30
db<>fiddle here
Build your validity and expiry dates/timestamps as you go, using OLAP functions.
WITH
indata(id,price,expiry) AS (
SELECT 1,11.00,DATE '30-Jan-2022'
UNION ALL SELECT 2,12.00,DATE '28-Feb-2022'
UNION ALL SELECT 3,13.00,DATE '30-Mar-2022'
UNION ALL SELECT 4,14.00,DATE '30-Apr-2022'
UNION ALL SELECT 5,15.00,NULL
)
,
enriched AS (
SELECT
id
, price
, LAG(NVL(expiry, '9999-12-31'),1,'0001-01-01') OVER(ORDER BY id) AS validity
, NVL(expiry, '9999-12-31') AS expiry
FROM indata
-- chk id | price | validity | expiry
-- chk ----+-------+------------+------------
-- chk 1 | 11.00 | 0001-01-01 | 2022-01-30
-- chk 2 | 12.00 | 2022-01-30 | 2022-02-28
-- chk 3 | 13.00 | 2022-02-28 | 2022-03-30
-- chk 4 | 14.00 | 2022-03-30 | 2022-04-30
-- chk 5 | 15.00 | 2022-04-30 | 9999-12-31
)
SELECT
price
FROM enriched
WHERE '2022-04-22' >= validity
AND '2022-04-22 < expiry
;
You can write a CASE WHEN statement and change the expiry column to max value 999-12-31(253402270022) if expiry is null then sort and get max expiry. Then you can execute the condition expiry > date_apply
WITH maxTariffs AS
(SELECT id,
(CASE
WHEN expiry IS NULL
THEN 253402270022
ELSE expiry
END) AS expiry
FROM tariffs)
SELECT * FROM tariffs WHERE id IN (SELECT id FROM maxTariffs WHERE expiry > DATE_APPLY ORDER BY expiry ASC ) LIMIT 1
Demo in DBfiddle
I have the following table:
| id | metrictimestamp | vehicleid |
+----+-----------------+-----------+
| 1 | 20180201025934 | 33089 |
| 2 | 20180201025955 | 34489 |
| 3 | 20180201025959 | 32040 |
I need to group by date(metrictimestamp) and count how many unrepeated "vehicleid" there is for each day, any sugestions?
You can use DISTINCT in your query:
SELECT COUNT(DISTINCT metrictimestamp) FROM yourTable
First you need to convert your metrictimestamp field into a date type that mysql understands:
STR_TO_DATE(metrictimestamp, '%Y%m%d%h%i%s')
next you need to extract the date portion of that field and give it an alias (date):
DATE(STR_TO_DATE(metrictimestamp, '%Y%m%d%h%i%s')) date
finally you need to group by the resultant date and the vehicleid and filter by repeated records (so only include singletons), so putting it all together:
select DATE(STR_TO_DATE(metrictimestamp, '%Y%m%d%h%i%s')) date, vehicleid from group_test_table group by date, vehicleid having count(vehicleid) = 1;
If I misunderstood your question and you only want the unique vehicleids for any date then:
select distinct DATE(STR_TO_DATE(metrictimestamp, '%Y%m%d%h%i%s')) date, vehicleid from group_test_table group by date, vehicleid;
Trying to create a running total for orders in SQL Server 2008, similar to the below table (Order No & Order Total columns exist in my SQL Server table), tried using a recursive cte but my results were a running total for all orders, not grouped by order no. Any suggestions how to have the running total grouped by the order no? Thanks
---------------------------------------------------------
| Order No. | Order Total | Running Total for Order No |
---------------------------------------------------------
| 1 | $10,000 | $10,000 |
---------------------------------------------------------
| 1 | -$5,000 | $5,000 |
---------------------------------------------------------
| 1 | $3,000 | $8,000 |
---------------------------------------------------------
| 2 | $2,500 | $2,500 |
---------------------------------------------------------
| 2 | $5,000 | $7,500 |
---------------------------------------------------------
| 2 | $4,000 | $11,000 |
---------------------------------------------------------
I would do this is with an Instead of Insert Trigger. The trigger would subtract/add from the groups first value. Obviously this should of been done at the creation of the table but you could add it after you make table update.
Keep in mind in order for the below code to work, you would need a primary key on the Order table
CREATE TABLE Orders
(
id INT IDENTITY(0, 1) PRIMARY KEY
, orderNo INT
, orderTotal MONEY
, runningTotal MONEY
);
INSERT INTO Orders
VALUES
(1,10000,10000),
(1,-5000,5000),
(1,3000,8000),
(2,2500,2500),
(2,5000,7500),
(2,4000,11500);
GO
--CREATE TRIGGER
CREATE TRIGGER trg_RunningTotal ON Orders
INSTEAD OF INSERT
AS
BEGIN
DECLARE #PreviousTotal MONEY =
(
SELECT TOP 1
a.runningTotal
FROM Orders AS a
INNER JOIN INSERTED AS b ON a.orderNo = b.orderNo
WHERE a.orderno = b.Orderno
ORDER BY a.id DESC
);
INSERT INTO Orders
SELECT
orderno,
orderTotal,
(#PreviousTotal + orderTotal) AS runningTotal
FROM INSERTED;
END;
--Insert new record
INSERT INTO orders
VALUES
(1,1000,NULL);
--View newly added record
SELECT
*
FROM orders
WHERE orderno = 1;
You need to following query:
SELECT orderno,
SUM((CASE WHEN ISNUMERIC(ordertotal)=1
THEN CONVERT(MONEY,ordertotal) ELSE 0 END)
)
AS [Converted to Numeric]
FROM price group by orderno
I have small database with couple of tables for some small PHP project. One of my SELECTs (given bellow table) gives result like this:
+-------+------+------+
| idpart | qty |IMEI |
+-------+------+------+
| 2 | 4 | xxx |
| 6 | 1 | yyyy |
| 8 | 2 | zzzz |
|10 | 3 | ssss |
+-------+------+------+
Number of rows changes it can be 1 or n, but its never less then 1. idpart is not repeating it self as result of this query - it can be show only once. This is the actual query:
select odel_part.idpart as idpart, model_part.qtyused as qty, reparationorders.IMEI as IMEI
from reparation orders
inner join order model on order_model.idreparationorder=reparationorders.idreparationorder
inner join models on order_model.idmodel = models.idmodel
inner join model_part on models.idmodel = model_part.idmodel
inner join parts on model_part.idpart = parts.idpart
where reparationorders.idreparationorder = 1
Result of this query along with some additional data which is fixed has to be inserted in to other table. Other table has following fields:
+-----------+-----------+-------+--------+-----+-------+
| idtrans | idpart | qty | date | tt | IMEI |
+-----------+-----------+-------+--------+-----+-------+
idtrans - int which autoincrements
idpart - from query (idpart)
qty - from query (qty)
date - entered manualy
tt - entered manualy
IMEI - from query (IMEI)
in this 2nd table idtrans is unique, idpart can repeat it self thorough rows (this is intended behaviour, because this table will track usage of this parts for different dates).
Can you help me with doing this insert to 2nd table (name of 2nd table is transactions)?
Thank you in advance
You would just do:
insert into transactions(idpart, qty, date, tt, IMEI)
select idpart, qty, now(), #tt, imei
from reparations orders . . .;
The . . . is just the rest of your query. I am guessing that you want the current date/time inserted for "date"; now() provides that information.
I currently have quite a messy query, which joins data from multiple tables involving two subqueries. I now have a requirement to group this data by DAY(), WEEK(), MONTH(), and QUARTER().
I have three tables: days, qos and employees. An employee is self-explanatory, a day is a summary of an employee's performance on a given day, and qos is a random quality inspection, which can be performed many times a day.
At the moment, I am selecting all employees, and LEFT JOINing day and qos, which works well. However, now, I need to group the data in order to breakdown a team or individual's performance over a date range.
Taking this data:
Employee
id | name
------------------
1 | Bob Smith
Day
id | employee_id | day_date | calls_taken
---------------------------------------------
1 | 1 | 2011-03-01 | 41
2 | 1 | 2011-03-02 | 24
3 | 1 | 2011-04-01 | 35
Qos
id | employee_id | qos_date | score
----------------------------------------
1 | 1 | 2011-03-03 | 85
2 | 1 | 2011-03-03 | 95
3 | 1 | 2011-04-01 | 91
If I were to start by grouping by DAY(), I would need to see the following results:
Day__date | Day__Employee__id | Day__calls | Day__qos_score
------------------------------------------------------------
2011-03-01 | 1 | 41 | NULL
2011-03-02 | 1 | 24 | NULL
2011-03-03 | 1 | NULL | 90
2011-04-01 | 1 | 35 | 91
As you see, Day__calls should be SUM(calls_taken) and Day__qos_score is AVG(score). I've tried using a similar method as above, but as the date isn't known until one of the tables has been joined, its only displaying a record where there's a day saved.
Is there any way of doing this, or am I going about things the wrong way?
Edit: As requested, here's what I've come up with so far. However, it only shows dates where there's a day.
SELECT COALESCE(`day`.day_date, qos.qos_date) AS Day__date,
employee.id AS Day__Employee__id,
`day`.calls_taken AS Day__Day__calls,
qos.score AS Day__Qos__score
FROM faults_employees `employee`
LEFT JOIN (SELECT `day`.employee_id AS employee_id,
SUM(`day`.calls_taken) AS `calls_in`,
FROM faults_days AS `day`
WHERE employee.id = 7
GROUP BY (`day`.day_date)
) AS `day`
ON `day`.employee_id = `employee`.id
LEFT JOIN (SELECT `qos`.employee_id AS employee_id,
AVG(qos.score) AS `score`
FROM faults_qos qos
WHERE employee.id = 7
GROUP BY (qos.qos_date)
) AS `qos`
ON `qos`.employee_id = `employee`.id AND `qos`.qos_date = `day`.day_date
WHERE employee.id = 7
GROUP BY Day__date
ORDER BY `day`.day_date ASC
The solution I'm comming up with looks like:
SELECT
`date`,
`employee_id`,
SUM(`union`.`calls_taken`) AS `calls_taken`,
AVG(`union`.`score`) AS `score`
FROM ( -- select from union table
(SELECT -- first select all calls taken, leaving qos_score null
`day`.`day_date` AS `date`,
`day`.`employee_id`,
`day`.`calls_taken`,
NULL AS `score`
FROM `employee`
LEFT JOIN
`day`
ON `day`.`employee_id` = `employee`.`id`
)
UNION -- union both tables
(
SELECT -- now select qos score, leaving calls taken null
`qos`.`qos_date` AS `date`,
`qos`.`employee_id`,
NULL AS `calls_taken`,
`qos`.`score`
FROM `employee`
LEFT JOIN
`qos`
ON `qos`.`employee_id` = `employee`.`id`
)
) `union`
GROUP BY `union`.`date` -- group union table by date
For the UNION to work, we have to set the qos_score field in the day table and the calls_taken field in the qos table to null. If we don't, both calls_taken and score would be selected into the same column by the UNION statement.
After this, I selected the required fields with the aggregation functions SUM() and AVG() from the union'd table, grouping by the date field in the union table.