MySQL query not quite right...probably pretty simple - mysql

I've got a table for a payroll system, with four fields, and some sample data:
pkref employee_id new_wage effective_date
===== =========== ======== ==============
23 06031-BOB 10 080101
37 06031-BOB 15 090501
90 06031-BOB 13 110228
When an employee's wage is changed, a primary key reference auto-increments, and the appropriate information is recorded (effective_date is a timestamp, yymmdd). No problems there.
Now, I'm trying to get a query to find
All entries related to an employee, then
The maximum effective_date stamp in those sub-entries
The wage that corresponds to that maximum.
I've made my very first subquery ever (!), got it almost right, but it's buggy. Could some guru have a look and give me a bump in the right direction?
SELECT MAX(effective_date),new_wage FROM (SELECT effective_date,new_wage FROM hr_wages WHERE employee_code='06031-BOB') AS t1
Ideally, I want 110228 and 13 to be returned. But, as the aforementioned guru will no doubt see immediately, something is wrong. The new_wage value does not always match the max effective_date.
So. Vat to do?

Hang on, what's stopping you from doing the following?
select effective_date, new_wage from hr_wages
where employee_code = '06031-BOB'
order by effective_date desc
limit 1

For only one employee you can use IN with subquery
SELECT new_wage, other data...
FROM hr_wages
WHERE effective_date IN (SELECT max(effective_date) FROM hr_wages WHERE employee_code='06031-BOB')
AND employee_code='06031-BOB' -- Corrected: Credits to Stev
e
For all employees
SELECT new_wage, CO
FROM hr_wages w LEFT JOIN
(SELECT max(effective_date) effective_date, employee_code
FROM hr_wages
GROUP BY employee_code
)d ON w.employee_code = d.employee.code AND w.effective_date = d.effective_date

There was a post from #niktrs that provided the right answer, but it's been deleted!
So, here's the query I was looking for, with #niktrs help. If his post comes back, I'll accept it.
SELECT new_wage FROM hr_wages WHERE effective_date IN
(SELECT effective_date,new_wage FROM hr_wages WHERE employee_code='06031-BOB')
AND employee_code='06031-BOB'
This ugliness can be beautified, I'm sure.

Related

Efficiently get latest appointment for every person sorted by oldest first

I already asked this question earlier but forgot a few (important) details or got them wrong.
My table in MySQL 8.0.29 looks like this
UserID
Appointment
Description
Bob
2022-06-01
Cleaning
Bob
2022-06-03
Toothache
John
2022-06-02
Braces
I'm trying to get the latest appointment for every person sorted by oldest first.
The query should return
UserID
Appointment
Description
John
2022-06-02
Braces
Bob
2022-06-03
Toothache
Using one of the previous answers I get
SELECT Name, Appointment, Description
FROM (
SELECT Name, Appointment, Description, ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Appointment DESC) rn) t1
WHERE rn = 1
The problem is the database currently has 3 million rows and it'll continue to grow so this query ends up being pretty slow.
My plan is to consume the data in chunks so I'd prefer the query having "pagination". Something like a LIMIT 0, 5000 to get 5000 records at a time.
I'm open to even re-architecting the database if it comes to that.
For now i've resorted to creating a new table that just keeps the latest appointment for each user.
You are halfway there. Use that query as a 'derived table' instead of making it permanent:
SELECT b.*
FROM ( SELECT user_id, MAX(appointment) AS last_date)
FROM tbl
GROUP BY user_id ) AS x
JOIN tbl AS b ON b.user_id = x.user_id
AND b.appointment = x.last_date
And be sure to have INDEX(user_id, appointment)
I would be interested to see if this and the "OVER" approach both give the same results and which is faster.

Retrieve rows that have a first entry in 2014 in MySQL

I want to retrieve all rows from a table that have their first entry on or after 01/01/2014 but no later than 31/12/2014
Example of the table:
OID FK_OID Treatment Trt_DATE
1 100 19304 2011-05-24
2 100 19304 2011-08-01
3 100 19306 2014-03-05
4 200 19305 2012-02-02
5 300 19308 2014-01-20
6 400 19308 2014-06-06
For example. I would like to pull all entries that have STARTED treatment in 2014. So above i would to extract FK_OID's 300 and 400 because their first entry is in 2014, but i would like to omit FK_OID 100 because they have 2 entries prior to 2014.
How do i go about this? I can extract all entries within a date range etc but that brings back all entries for that date and doesn't omit anyone who has an entry prior to the start of the date range. It just returns their first entry in 2014.
For the ones who need to see that i have tried something. See below.
I am not an experienced coder and this is the best i can get because i don't have the knowledge.
SELECT
mod,
(select NHSNum from person p
WHERE
p.oid = t.fk_oid) as 'NHS'
FROM
timeline t
Where trt_date BETWEEN '2014-01-01' AND '2014-12-31'
ORDER BY trt_date ASC
This returns every treatment for 2014 regardless of whether it is the first ever one for that person. I want to omit anyone from this list who has had treatment before 01/01/2014 as well as only return the first treatment per person. For example, this code returns all treatments for all people in 2014. I only want their first one and only if it is their first one ever.
Thanks.
create table aThing
( oid int auto_increment primary key,
fk_oid int not null,
treatment int not null,
trt_date date not null
);
insert aThing (fk_oid,treatment,trt_date) values
(100, 19304, '2011-05-24'),
(100, 19304, '2011-08-01'),
(100, 19306, '2014-03-05'),
(200, 19305, '2012-02-02'),
(300, 19308, '2014-01-20'),
(400, 19308, '2014-06-06');
select fk_oid,dt
from
( select fk_oid,min(trt_date) as dt
from aThing
group by fk_oid
) xDerived
where year(dt)=2014;
+--------+------------+
| fk_oid | dt |
+--------+------------+
| 300 | 2014-01-20 |
| 400 | 2014-06-06 |
+--------+------------+
The inner part, the nested one, become a derived table, and is given a name xDerived. This means that even though it is just a result set, by making it a derived table, it can be referred to by name. So it is not a physical table, but a derived one, or virtual one.
So that derived table is a very simple group by with an aggregate function. It says, for every fk_oid, bring back one row and only 1 row, with its minimum value for trt_date.
So if you have 10 million rows in that table called aThing, but only 17 distinct values for fk_oid, it will return only 17 rows. Each row being the minimum of trt_date for its fk_oid.
So now that that is achieved, the outer wrapper says just show me those two columns (but with a year check). There is a complicated to explain reason why I had to do that, so I will try to do it here.
But I might need a little time to explain it well, so bear with me.
This will be a shortcut way to say it. I had to get the min into an alias, and I only had access to that alias if resolved in a derived table, to cleanse it so to speak, and then access it with an outer wrapper.
An alias of aggregate column, like as dt, is not available (as a pseudo like column name which is what an alias is) ... it is not available in a where clause. But by wrapping it in a derived table name, I cleanse it so to speak, and then I can access it in a where clause.
So I can't access it directly in its own query in the where clause, but when I wrap it in an envelope (a derived table), I can access it on the outside.
I will try better to explain it later, maybe, but I would have to show alternative attempts to gain access to results, and the syntax errors that would result.
There's probably a more elegant solution, but this seems to satisfy the requirement...
SELECT x.*
FROM my_table x
JOIN
( SELECT fk_oid
, MIN(trt_date) min_date
FROM my_table
GROUP
BY fk_oid
HAVING min_date > '2014-01-01'
) a
ON a.fk_oid = x.fk_oid
LEFT
JOIN my_table b
ON b.fk_oid = a.fk_oid
AND b.trt_date > '2014-12-31'
WHERE b.oid IS NULL;
Having a few years a experience with this, i decided to revisit it. The solution i now use regularly is:
SELECT t1.column1, t1.column2
FROM MyTable AS t1
LEFT OUTER JOIN MyTable AS t2
ON t1.fkoid = t2.fkoid
AND (t1.date > t2.date
OR (t1.date = t2.date AND t1.oid > t2.oId))
WHERE t2.fkoid IS NULL and t1.date >= '2014-01-01'

Finding The Sum OF Two Values In SQL Server 2008

I have a table Usage and it contains the following columns
sl_No
usage_ID
energyItem_ID
qty
unit_ID
location_ID
p_Rate
Sometimes the same EnergyItem might be located at different locations..
During those conditions how can I get the sum of qty of an individual energyItem..
How to get the sum of the qty of energyItems?
If I've understood correctly, you're trying to find the quantity of each
energy item, regardless of its location, using information in a single table.
The following query will give you the energyItem_ID of each item followed by the total quantity of each item:
SELECT energyItem_ID,Sum(qty) as TotalQuantity
FROM Usage
GROUP BY energyItem_ID
ORDER BY energyItem_ID
If, on the other hand, you wanted the quantity of each energy item, broken down by location, you would need the following:
SELECT location_ID,energyItemID,Sum(qty) as QuantityByLocation
FROM Usage
GROUP BY location_ID,energyItemID
ORDER BY location_ID,energyItemID
The order by clauses make the result easier to follow, but are not strictly necessary.
Finally, the answer by marc_s will give you the quantity of a specific energyItem.
How about:
SELECT EnergyItem_ID, SUM(qty)
FROM dbo.Usage
WHERE EnergyItem_ID = 42 -- or whatever ....
GROUP BY EnergyItem_ID
Or what are you looking for?? The question isn't very clear on the expected output....
select a.usage_ID , b.sum(p_Rate) as total from Table_1 a
inner join Table_2 as b on a.usage_ID = b.usage_ID
group by a.usage_ID

Getting a sorted distinct list from mySQL

Goal
I'l like to get a list of unique FID's ordered by the the one which has most recently been changed. In this sample table it should return FIDs in the order of 150, 194, 122
Example Data
ID FID changeDate
----------------------------------------------
1 194 2010-04-01
2 122 2010-04-02
3 194 2010-04-03
4 150 2010-04-04
My Attempt
I thought distinct and order by would do the trick. I initially tried:
SELECT distinct `FID` FROM `tblHistory` WHERE 1 ORDER BY changeDate desc
# Returns 150, 122, 194
using GROUP BY has the same result. I'm just barely a SQL amateur, and I'm a bit hung up. What seems to be happening is the aggregating functions find the first occurrence of each and then perform the sort.
Is there a way I can get the result I want straight from mySQL or do I have to grab all the data and then sort it in the PHP?
This worked for me:
SELECT FID
FROM tblHistory
GROUP BY FID
ORDER BY MAX(changeDate) DESC;
Okay, been reading questions on the site since I asked this one, and came up with an answer that seems to work, although I hope perhaps someone else may shed light on a simpler way:
SELECT t1.FID, t1.CD
FROM (
SELECT FID, max(changeDate) as CD
FROM sorted
GROUP BY FID
) as t1
WHERE 1
order by t1.CD desc
Seems to get the results I expect. I didn't know subqueries existed until a few minuets ago. I'm a real SQL newbie.
select * from tbl A
where changeDate =
(select max(changeDate) from tbl where tbl.fid = A.fid)
order by changeDate desc

mysql first record retrieval

While very easy to do in Perl or PHP, I cannot figure how to use mysql only to extract the first unique occurence of a record.
For example, given the following table:
Name Date Time Sale
John 2010-09-12 10:22:22 500
Bill 2010-08-12 09:22:37 2000
John 2010-09-13 10:22:22 500
Sue 2010-09-01 09:07:21 1000
Bill 2010-07-25 11:23:23 2000
Sue 2010-06-24 13:23:45 1000
I would like to extract the first record for each individual in asc time order.
After sorting the table is ascending time order, I need to extract the first unique record by name.
So the output would be :
Name Date Time Sale
John 2010-09-12 10:22:22 500
Bill 2010-07-25 11:23:23 2000
Sue 2010-06-24 13:23:45 1000
Is this doable in an easy fashion with mySQL?
I think that something along the lines of
select name, date, time, sale from mytable order by date, time group by name;
will get you what you're looking for
you need to perform a groupwise max or groupwise min
see below or http://pastie.org/973117 for an example
select
u.user_id,
u.username,
latest.comment_id
from
users u
left outer join
(
select
max(comment_id) as comment_id,
user_id
from
user_comment
group by
user_id
) latest on u.user_id = latest.user_id;
In databases, there really is no "first" or "last" record; think of each record as its own, non-positional entity in the table. The only positions they have are when you give them one, say, using ORDER BY.
This will give you what you want. It might not be efficient, but it works.
select Name, Date, Time, Sale from
(select Name, Date, Time, Sale from MyTable
order by Date asc, Time asc) MyTable_subquery_name
group by Name
Note: MyTable_subquery_name is just a dummy name for the subquery. MySQL will give the error ERROR 1248 (42000): Every derived table must have its own alias without it.
If only GROUP BY and ORDER BY were communicative operations, then this wouldn't have to be a subquery.