Hi I am looking for a solution to my inability to understand how I can get an overall total for a column in my query.
This query gets engineers names and the number of jobs they have that are out of SLA i.e. the data the job should have been completed has past and the job has still to be completed.
SELECT Engineer,Job_Status,COUNT(*) as 'Out Of SLA'
FROM import
WHERE (Job_Status = 'P' or Job_Status='P2' or Job_Status='P8')
and (isnull(Job_Completed_Date)
or Job_Completed_Date='0000-00-00')
and (Job_SLA_Due_Date < CURDATE()
)
GROUP BY import.Engineer,Job_Status
The code above produces the following results from the import table.
+----------------+------------+------------+
| Engineer | Job_Status | Out of SLA |
+----------------+------------+------------+
| Andy Beeres | P | 15 |
| Andy Broad | P | 4 |
| Darren Goodwin | P | 6 |
+----------------+------------+------------+
I want to be able to show the total number of the Out of SLA column as well as the rest of the table data if that makes sense something like the table below.
| Engineer | Job_Status | Out of SLA |
|------------- |------------ |------------ |
| Andy Beeres | P | 14 |
| | P2 | 3 |
| | P8 | 1 |
| Total | | 18 |
| Andy Broad | P | 12 |
| | P2 | 2 |
| Total | | 14 |
| Grand Total | | 32 |
Regards
Alan
Use with rollup with group by to get total_sla
According to MySql Docs:
The GROUP BY clause permits a WITH ROLLUP modifier that causes summary output to include extra rows that represent higher-level (that is, super-aggregate) summary operations. ROLLUP thus enables you to answer questions at multiple levels of analysis with a single query.
SELECT Engineer,Job_Status,COUNT(*) as 'Out Of SLA'
FROM import
WHERE (Job_Status = 'P' or Job_Status='P2' or Job_Status='P8')
and (isnull(Job_Completed_Date)
or Job_Completed_Date='0000-00-00')
and (Job_SLA_Due_Date < CURDATE()
)
GROUP BY import.Engineer,Job_Status WITH ROLLUP
One option is to use a subquery which finds the SLA total:
SELECT Engineer,
Job_Status,
COUNT(*) AS `Out Of SLA`,
(SELECT COUNT(*) FROM import) AS total_sla
FROM import
WHERE (Job_Status = 'P' OR Job_Status='P2' OR Job_Status='P8') AND
(ISNULL(Job_Completed_Date) OR Job_Completed_Date = '0000-00-00') AND
Job_SLA_Due_Date < CURDATE()
GROUP BY Engineer,
Job_Status
Related
I have two tables i.e vehicle and vehicle_maintenance.
vehicle
-----------------------------------
| v_id | v_name | v_no |
-----------------------------------
| 1 | car1 | car123 |
-----------------------------------
| 2 | car2 | car456 |
-----------------------------------
vehicle_maintenance
-----------------------------------------------------------------------
| v_main_id | v_id | v_main_date | v_main_remainder |
-----------------------------------------------------------------------
| 1 | 1 | 2020/10/10 | 1 |
| 2 | 1 | 2020/10/20 | 2 |
| 3 | 2 | 2020/10/04 | 365 |
| 4 | 2 | 2020/10/15 | 5 |
-----------------------------------------------------------------------
I want to get each car maintenance details i.e car2 maintenance date is 2020/10/15 and i want to check next maintenance date based on v_main_remainder field. That means next maintenance date will be 2020/10/20 ( add 5 day to the maintenance date). I want to also calculate the no of days left from next maintenance date. Suppose today is 2020/10/10 then it will show 10 days left.
Here is my query
SELECT
v.v_id,
v.v_name,
v.v_no,
max(vm.v_main_date) as renewal_date,
datediff(
DATE_ADD(
max(vm.v_main_date), INTERVAL +vm.v_main_remainder day
),
now()
) as day_left
FROM vehicle as v, vehicle_maintenance as vm
GROUP BY v.v_id
But the problem is vm.v_main_remainder in date_add function taken from first row.
Here is the result
-----------------------------------------------------------------------
| v_id | v_name | v_no | renewal_date | day_left |
-----------------------------------------------------------------------
| 1 | car1 | car123 | 2020/10/20 | 11 |
-----------------------------------------------------------------------
| 2 | car2 | car456 | 2020/10/15 | 370 |
-----------------------------------------------------------------------
As a starter, your query is obviously missing a join condition between the two tables, so that's a cartesian product. This type of problem is much easier to spot when using explicit joins.
Then: you want to filter on the latest maintenance record per car, so aggregation is not appropriate.
One option uses window functions, available in MySQL 8.0:
select v.v_id, v.v_name, v.v_no, vm.v_main_date as renewal_date,
datediff(vm.v_main_date + interval vm.v_main_remainder day, current_date) as day_left
from vehicle as v
inner join (
select vm.*, row_number() over(partition by v_id order by v_main_date desc) rn
from vehicle_maintenance
) as vm on vm.v_id = v.v_id
where vm.rn = 1
Note that I changed now() to current_date, so datediff() works consistently on dates rather than datetimes.
Context:
I'm attempting to take a series of market transactions, and determine the amount of money actually moving per item type. This is pretty much my first attempt at MySql, so the query is ugly, but the following nearly works:
SELECT types.typename,
averages.type,
averages.price,
movement.sold,
( averages.price * movement.sold ) AS value
FROM (SELECT type,
Round(Avg(price)) AS price
FROM orders
GROUP BY type) AS averages
INNER JOIN (SELECT type,
( startingvolume - currentvolume ) AS sold
FROM (SELECT type,
Sum(volume) AS currentVolume,
Sum(volumeentered) startingVolume
FROM orders
GROUP BY type) AS movement
WHERE ( startingvolume - currentvolume ) > 10000
ORDER BY sold) AS movement
ON averages.type = movement.type
INNER JOIN invtypes AS types
ON types.typeid = averages.type
ORDER BY value DESC
LIMIT 10 ;
-
+------------------------------------+-------+---------+------------+------------------+
| typeName | type | price | sold | value |
+------------------------------------+-------+---------+------------+------------------+
| Dirt | 34 | 1904767 | 2670581874 | 5086836224393358 |
| Light Wood | 2629 | 42999 | 2756595 | 118530828405 |
| Dark Wood | 24509 | 47344 | 1107771 | 52446310224 |
| Stone | 21922 | 18386 | 1505884 | 27687183224 |
| Grass | 238 | 5643 | 4554470 | 25700874210 |
| Paper | 3814 | 25635 | 861006 | 22071888810 |
| Iron | 3699 | 320270 | 58833 | 18842444910 |
| Ink | 16275 | 8552 | 2200545 | 18819060840 |
| Loam | 2679 | 5759 | 2608771 | 15023912189 |
| Copper | 672 | 904612 | 14989 | 13559229268 |
+------------------------------------+-------+---------+------------+------------------+
The problem with the data above is that the raw market data is unavoidably corrupted by outliers, as you can see below:
select type, price from orders where type = 34 order by price desc limit 10;
-
+------+-----------+
| type | price |
+------+-----------+
| 34 | 200000000 |
| 34 | 15.99 |
| 34 | 12.06 |
| 34 | 10 |
| 34 | 7.67 |
| 34 | 7.5 |
| 34 | 7.3 |
| 34 | 7.17 |
| 34 | 7.1 |
| 34 | 7.06 |
+------+-----------+
Core problem:
99% of the market data is clean, but the outliers destroy the average, and MySql doesn't seem to have a median function. I've found several examples of how to find the median of an entire column, but I need the median per-item.
How would I determine a per-item median in stead of a per-item mean, or efficiently clean the data of these outliers prior to running the primary query?
Note:
I've tried omitting results via std, but prices of items range from $17 to $10B, while deviation remains relatively low, regardless of price range.
I won't touch your original query because it very complex, but one option you could do would be to use a subquery to remove any statistical outliers. For example, if you wanted to remove any outlier from the orders table whose value is more than say two standard deviations away from the mean you could use:
SELECT t1.type,
t1.price
FROM orders t1
INNER JOIN
(
SELECT type,
AVG(price) AS AVG,
STD(price) AS STD
FROM orders
GROUP BY type
) t2
ON t1.type = t2.type
WHERE t1.price < ABS(2*t2.STD - t2.AVG) -- any value more than 2 standard devations
-- away from the mean is discarded
Demo here:
SQLFiddle
I have a staff table like this --->
+------+------------------+------+------------+--------+
| EC | Name | Code | Dob | Salary |
+------+------------------+------+------------+--------+
| 2001 | ROBBIE KEANE | VSS1 | 1990-05-16 | 18000 |
| 2002 | ANSUMAN BANERJEE | VSS1 | 1985-05-21 | 18000 |
| 2003 | OMAR GONZALEZ | SACP | 1989-04-16 | 20000 |
| 2004 | ALAN GORDON | IALO | 1989-05-03 | 20000 |
| 2005 | ROBBIE KEANE | IALO | 1988-01-16 | 18000 |
| 2006 | CHANDLER HOFFMAN | BBDP | 1988-07-17 | 22000 |
| 2007 | PAUL POGBA | BHSM | 1990-08-16 | 18000 |
| 2008 | SHINJI KAGAWA | LPDC | 1991-01-20 | 18000 |
+------+------------------+------+------------+--------+
And now i want to list those codes (like VSS1), which have less than specified number of people assigned with them(say like less than 2) , how can i do this please help.
My query up till now is-->
SELECT Code,count(*) as 'Number of Staff' from STAFF where Code IN (SELECT Code from STAFF GROUP BY CODE LIMIT 2);
But this is not working.
You can filter row count for each Code group with the HAVING clause :
SELECT Code
, COUNT(*)
FROM STAFF
GROUP BY Code
HAVING COUNT(*) < 2
If you need to know the names of the people having this count less than 2 then...
SELECT S.EC, S.Name, S.Code, S.DOB, S.Salary, SC.Code, SC.Cnt
FROM STAFF S
INNER JOIN (SELECT Count(*) cnt, Code FROM STAFF GROUP BY CODE) SC
on S.Code = SC.code
WHERE SC.CNT < 2
should work in SQL server and mySQL. Though SQL Sever could also use a windowed set which would be faster.
If however, you just need to know the Codes having less than a certain number, notulysses having clause should fit the bill.
Using table below, How would get a column for 5 period moving average, 10 period moving average, 5 period exponential moving average.
+--------+------------+
| price | data_date |
+--------+------------+
| 122.29 | 2009-10-08 |
| 122.78 | 2009-10-07 |
| 121.35 | 2009-10-06 |
| 119.75 | 2009-10-05 |
| 119.02 | 2009-10-02 |
| 117.90 | 2009-10-01 |
| 119.61 | 2009-09-30 |
| 118.81 | 2009-09-29 |
| 119.33 | 2009-09-28 |
| 121.08 | 2009-09-25 |
+--------+------------+
The 5-row moving average in your example won't work. The LIMIT operator applies to the return set, not the rows being considered for the aggregates, so changing it makes no difference to the aggregate values.
SELECT AVG(a.price) FROM (SELECT price FROM t1 WHERE data_date <= ? ORDER BY data_date DESC LIMIT 5) AS a;
Replace ? with the date whose MA you need.
SELECT t1.data_date,
( SELECT SUM(t2.price) / COUNT(t2.price) as MA5 FROM mytable AS t2 WHERE DATEDIFF(t1.data_date, t2.data_date) BETWEEN 0 AND 6 )
FROM mytable AS t1 ORDER BY t1.data_date;
Change 6 to 13 for 10-day MA
I'm having trouble with a Mysql Query that require some "complex" ordering.
I've 2 tables:
Training
+--------------+------------------+
| training_id | training_name |
+--------------+------------------+
| 1 | test1 |
| 2 | test2 |
| 3 | test3 |
+--------------+------------------+
Training_venue
+----------+--------------+------------+
| venue_id | training_id | venue_date |
+----------+--------------+------------+
| 1 | 2 | 2009-06-01 |
| 2 | 2 | 2012-06-01 |
| 3 | 2 | 2011-06-01 |
| 4 | 1 | 2009-09-01 |
| 5 | 1 | 2011-09-01 |
| 6 | 1 | 2012-09-01 |
| 7 | 3 | 2009-01-01 |
+----------+--------------+------------+
And I'm expecting the following results:
+--------------+------------------+------------+--------------+
| training_id | training_name | venue_id | venue_date |
+--------------+------------------+------------+--------------+
| 2 | test2 | 2 | 2011-06-01 |
| 2 | test2 | 3 | 2012-06-01 |
| 1 | test1 | 6 | 2011-09-01 |
| 1 | test1 | 5 | 2012-09-01 |
+--------------+------------------+------------+--------------+
As you can see, the result requirement are:
A training with no future venue is discarded.
Expired venue are discarded
The trainings are "grouped" together
The trainings with the soonest venue is first, the training with the "latest soonest venue" is last
Inside the training, the venues are ordered from the soonest to the latest.
What mysql query will return that result set ?
Edit:
Here's what I've tried so far:
SELECT *
FROM `training` AS t
LEFT JOIN `training_venue` AS v USING ( `training_id` )
WHERE `venue_date` >= NOW()
ORDER BY `training_id;
But if the order by training_id take care of keeping all the training "grouped" together, it doesnt permit to order the training from the training with the soonest venue to the training with the lastest venue.
I also edited the data table to illustrate that problematic. See how the results are ordered, it's not by training_id, but by soonest venue.
Edit:
Corrected the dates.
SELECT t1.training_id, t1.training_name, t2.venue_id, t2.venue_date
FROM Training t1
INNER JOIN Training_venue t2 ON t1.training_id = t2.training_id
WHERE t2.venue_date >= NOW()
ORDER BY t1.training_id ASC, t2.venue_date ASC
I don't know what your last point is getting at: "Inside the training, the venues are ordered from the soonest to the latest." But the above query seems to match the rest of your needs.
EDIT: I now sort of understand better what you are after. And it is a tad complicated I think. I'll have another think about it.
EDIT: I think I have it!
SELECT t1.training_id, t1.training_name, t2.venue_id, t2.venue_date
FROM Training t1
INNER JOIN (SELECT training_id, venue_date
FROM training_venue
WHERE venue_date >= NOW()
GROUP BY training_id
ORDER BY MIN(venue_date)) t3 ON t1.training_id = t3.training_id
INNER JOIN Training_venue t2 ON t1.training_id = t2.training_id
WHERE t2.venue_date >= NOW()
ORDER BY t3.venue_date DESC, t2.venue_date ASC
Try it!
EDIT: Was using '2010-01-01' instead of NOW() as NOW() would lose the 2010 dates that you seemed to want included.