Need SQL Query to fetch data from a table for MYSQL - mysql

I have a table where there are columns like ID, USER, PreviousValue, CurrentValue, Date.
Need a query that will give the latest current value and the rest columns could be of any user based on user queried for.
Example: If the table has last entry for user A, and query is for User B, The ID, User, previous Value, Date should be returned for user B but the current Value should be for user A.
ID Previous Current User createdOn
1 RED BLUE System 14-MAR-2020
2 GREEN YELLOW ADMIN 12-MAR-2020
IF I query for ADMIN as user the row returned should contain below data:
ID Previous Current User createdOn
2 GREEN BLUE ADMIN 12-MAR-2020
As the latest row was added on 14 MARCH the current value should come from that row, rest all data should be of the user I queried for.

WARNING! Date Format Problem
I strongly urge you to convert the CreadedOn column to a DATE data-type, instead of VARCHAR, in order to retrieve the appropriate ordering of values by date.
Otherwise 14-MAR-2020 will be considered later than 11-DEC-2020.
Issue Example DB-Fiddle
Schema
CREATE TABLE users (
`ID` INTEGER,
`Previous` VARCHAR(5),
`Current` VARCHAR(6),
`User` VARCHAR(18),
`createdOn` VARCHAR(20)
);
INSERT INTO users
(`ID`, `Previous`, `Current`, `User`, `createdOn`)
VALUES
('1', 'RED', 'BLUE', 'System', '14-MAR-2020'),
('2', 'GREEN', 'YELLOW', 'ADMIN', '12-MAR-2020'),
('3', 'GREEN', 'PURPLE', 'System', '11-DEC-2020'),
('4', 'GREEN', 'YELLOW', 'System', '10-MAR-2020');
Other answer query https://stackoverflow.com/a/61316029/1144627
select
Id,
Previous,
User,
CreatedOn,
(
select Current
from users
order by CreatedOn desc
limit 1
) as Current
from users
where `user` = 'ADMIN'
order by createdon desc
limit 1;
| Id | Previous | User | CreatedOn | Current |
| --- | -------- | ----- | ----------- | ------- |
| 2 | GREEN | ADMIN | 12-MAR-2020 | BLUE |
Expected Current of PURPLE
To fix the issue with the date sorting, you will need to modify your table using the STR_TO_DATE() function.
It is important to note that comparing with STR_TO_DATE in your query instead of updating the column will cause a full-table scan, comparing every record in the table.
Example DB-Fiddle
ALTER TABLE users
ADD COLUMN createdOn_Date DATE NULL DEFAULT NULL;
UPDATE users
SET CreatedOn_Date = STR_TO_DATE(CreatedOn, '%d-%b-%Y')
WHERE CreatedOn_Date IS NULL;
ALTER TABLE users
DROP COLUMN CreatedOn,
CHANGE COLUMN CreatedOn_Date CreatedOn DATE;
Then display your records in your desired format, use the DATE_FORMAT() function
Other Answer Query https://stackoverflow.com/a/61316029/1144627
select
Id,
Previous,
User,
DATE_FORMAT(CreatedOn, '%d-%b-%Y') AS CreatedOn,
(
select Current
from users
order by CreatedOn desc
limit 1
) as Current
from users
where `user` = 'ADMIN'
order by createdon desc
limit 1;
Result
| Id | Previous | User | CreatedOn | Current |
| --- | -------- | ----- | ----------- | ------- |
| 2 | GREEN | ADMIN | 12-Mar-2020 | PURPLE |

A subquery to get the latest current value should do it.
select
Id,
Previous,
User,
CreatedOn,
(
select Current
from users
order by CreatedOn desc
limit 1
) as Current
from users
where user = ?
order by createdon desc
limit 1

Related

Query to find an entry between dates

I have a table containing several records associated to the same entities. Two of the fields are dates - start and end dates of a specific period.
Example:
ID
Name
Start
End
3
Fred
2022/01/01
2100/12/31
2
John
2018/01/01
2021/12/31
1
Mark
2014/03/22
2017/12/31
The dates and names vary, but the only rule is that there are NO OVERLAPS - it's a succession of people in charge of a unique role, so there is only one record which is valid for any date.
I have a query returning me a date (let's call it $ThatDay) and what I am trying to do is to find a way to find which name it was at that specific date. For example, if the date was July 4th, 2019, the result of the query I am after would be "John"
I have run out of ideas on how to structure a query to help me find it. Thank you in advance for any help!
you can use a SELECT with BETWEEN as WHERE clause
The date format of MySQL is yyyy-mm-dd , if you keep that you wil never have problems
CREATE TABLE datetab (
`ID` INTEGER,
`Name` VARCHAR(4),
`Start` DATETIME,
`End` DATETIME
);
INSERT INTO datetab
(`ID`, `Name`, `Start`, `End`)
VALUES
('3', 'Fred', '2022/01/01', '2100/12/31'),
('2', 'John', '2018/01/01', '2021/12/31'),
('1', 'Mark', '2014/03/22', '2017/12/31');
SELECT `Name` FROM datetab WHERE '2019-07-04' BETWEEN `Start` AND `End`
| Name |
| :--- |
| John |
db<>fiddle here
If ou have a (Sub)- Query with a date as result,you can join it for example
SELECT `Name`
FROM datetab CROSS JOIN (SELECT '2019-07-04' as mydate FROM dual) t1
WHERE mydate BETWEEN `Start` AND `End`
| Name |
| :--- |
| John |
db<>fiddle here
Also when the query only return one row and column you can use the subquery like this
SELECT `Name`
FROM datetab
WHERE (SELECT '2019-07-04' as mydate FROM dual) BETWEEN `Start` AND `End`
| Name |
| :--- |
| John |
db<>fiddle here
Select where the result of your find-date query is between start and end:
select * from mytable
where (<my find date query>)
between start and end

Modify query to only return users who joined in certain YEAR

I am doing an excercise for college and any time I enter further than is already here, from a previous query the red x with the explanation is disallowing me to continue
the question is
Modify query so it only returns users who joined in 2020. List users by the date they joined.
code I tried is :
SELECT sname, fname, username,
cast(datejoined as date) as datejoined from user
Order by sname, datejoined;
I think I must change the whole date to just year but dont know how to do so - does anyone have any suggestions?? Thanks!
You can in MySQL use HAVING with the YEAR of your date column.
but why have you to cast it as DATE, save it as Date or datetime and you can use the column without cast it
CREATE TABLE user(sname varchar(50),fname varchar(50),username varchar(50),datejoined varchar(19))
INSERT INTO user VALUEs ('a','b','c','2021-01-01'),('a1','b1','c1','2020-01-01')
SELECT sname, fname, username,
cast(datejoined as date) as datejoined from user
HAVING YEAR(datejoined) = 2021
Order by sname, datejoined;
sname | fname | username | datejoined
:---- | :---- | :------- | :---------
a | b | c | 2021-01-01
db<>fiddle here

How to Query this in mysql

I have question about querying. I need to sum total of admin per account that active / logged less than 15 days from now. The problem is one account can have many admin and on admin table have "last login" field with date.
So we want to get is all admin that not logged for less 15 days from now.. so if one of the admin logged in 15 days from now will not count...
Example:
Account Table
id | Account name
------------------
1 | Tiger company
-------------------
2 | Bear Company
Admin Table
id | Account ID | admin name | last login
-------------------------------------------
1 | 1 | Billy Tiger | 09-01-2018
2 | 1 | Shirley | 09-22-2018
3 | 2 | John Bear | 09-06-2018
4 | 2 | Kyle Bear | 09-08-2018
So based on above data if today 09-26-2018 then I need to get the total number per account and sum it that the admin per site is not or never login below 15 days from 09-26-2018 which is below 09-11-2018, so if one of the admin is logged will be not counted.
So from the example above.. what I want to get the total sum is "1" so basically per account.. hy "1" because the tiger company admin...t he "shirley" has been logged on 09-22-2018 so it's active... so not counted it and the Bear company none of the admin logged after 09-11-2018 so it count as 1...
I hope it example explains it well.. sorry for being all confusing.. is it possible to do that in one query? and like select sum(id) as 'total' ....
use sub-query , you want to filter those account who's any id login last 15 days so 1st i find those id who login with in last 15 days and filtered then count according to accountid
CREATE TABLE `Account` (
`id` int(11) PRIMARY KEY NOT NULL,
`Accountname` varchar(25) NOT NULL
);
CREATE TABLE `Admin` (
`id` int(11) PRIMARY KEY NOT NULL,
`Account_ID` int(11) NOT NULL,
`Admin_Name` varchar(25) NOT NULL,
`Last_Login` date NOT NULL
);
INSERT INTO Account
VALUES (1, 'Tiger Company'), (2, 'Bear Company');
INSERT INTO `Admin`
VALUES (1, 1, 'Billy Tiger', '2018-09-01'),
(2, 1, 'Shirley', '2018-09-22'),
(3, 2, 'John Bear', '2018-09-06'),
(4, 2, 'Kyle Bear','2018-09-08' );
select t1.Account_ID
,a1.Accountname,
count(distinct t1.Account_ID) as total from
(
select a.* from Admin a
left join
(
select distinct Account_ID from Admin
where Last_Login>=DATE_SUB(CURDATE(), INTERVAL 15 DAY)
) t on a.Account_ID=t.Account_ID
where t.Account_ID is null
) t1 join Account a1 on t1.Account_ID =a1.id group by t1.Account_ID,a1.AccountName
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=baa13620bccbf6a70f6e0fc7d6e8d199
Account_ID Accountname total
2 Bear Company 1
Easiest and probably the most efficient way at scale would be to use a JOIN between the two tables:
SELECT `account`.`id`, `account`.`Account name`, COUNT(1) as 'Active Admins'
FROM `admin`
JOIN `account` ON `account`.`id` = `admin`.`Account_ID`
WHERE `admin`.`Last_Login` >= DATE_ADD(NOW(), INTERVAL -15 DAY)
GROUP BY `account`.`id`
Result:
| id | Account name | Active Admins |
|----|---------------|---------------|
| 1 | Tiger Company | 1 |
SQL Fiddle

How do I get a left join with a group by clause to return all the rows?

I am trying to write a query to determine how much of my inventory is committed at a given time, i.e. current, next month, etc.
A simplified example:
I have an inventory table of items. I have an offer table that specifies the customer, when the offer starts, and when the offer expires. I have a third table that associates the two.
create table inventory
(id int not null auto_increment , name varchar(32) not null, primary key(id));
create table offer
(id int not null auto_increment , customer_name varchar(32) not null, starts_at datetime not null, expires_at datetime, primary key (id));
create table items
(id int not null auto_increment, inventory_id int not null, offer_id int not null, primary key (id),
CONSTRAINT fk_item__offer FOREIGN KEY (offer_id) REFERENCES offer(id),
CONSTRAINT fk_item__inventory FOREIGN KEY (inventory_id) REFERENCES inventory(id));
create some inventory
insert into inventory(name)
values ('item 1'), ('item 2'),('item 3');
create two offers for this month
insert into offer(customer_name, starts_at)
values ('customer 1', DATE_FORMAT(NOW(), '%Y-%m-01')), ('customer 2', DATE_FORMAT(NOW(), '%Y-%m-01'));
and one for next month
insert into offer(customer_name, starts_at)
values ('customer 3', DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01'));
Now add some items to each offer
insert into items(inventory_id, offer_id)
values (1,1), (2,1), (2,2), (3,3);
What I want is a query that will show me all the inventory and the count of the committed inventory for this month. Inventory would be considered committed if the starts_at is less than or equal to now, and the offer has not expired (expires_at is null or expires_at is in the future)
The results I would expect would look like this:
+----+--------+---------------------+
| id | name | committed_inventory |
+----+--------+---------------------+
| 1 | item 1 | 1 |
| 2 | item 2 | 2 |
| 3 | item 3 | 0 |
+----+--------+---------------------+
3 rows in set (0.00 sec)
The query that I felt should work is:
SELECT inventory.id
, inventory.name
, count(items.id) as committed_inventory
FROM inventory
LEFT JOIN items
ON items.inventory_id = inventory.id
LEFT JOIN offer
ON offer.id = items.offer_id
WHERE (offer.starts_at IS NULL OR offer.starts_at <= NOW())
AND (offer.expires_at IS NULL OR offer.expires_at > NOW())
GROUP BY inventory.id, inventory.name;
However, the results from this query does not include the third item. What I get is this:
+----+--------+---------------------+
| id | name | committed_inventory |
+----+--------+---------------------+
| 1 | item 1 | 1 |
| 2 | item 2 | 2 |
+----+--------+---------------------+
2 rows in set (0.00 sec)
I cannot figure out how to get the third inventory item to show. Since inventory is the driving table in the outer joins, I thought that it should always show.
The problem is the where clause. Try this:
SELECT inventory.id
, inventory.name
, count(offers.id) as committed_inventory
FROM inventory
LEFT JOIN items
ON items.inventory_id = inventory.id
LEFT JOIN offer
ON offer.id = items.offer_id and
(offer.starts_at <= NOW() or
offer.expires_at > NOW()
)
GROUP BY inventory.id, inventory.name;
The problem is that you get a matching offer, but it isn't currently valid. So, the where clause fails because the offer dates are not NULL (there is a match) and the date comparison fails because the offer is not current ly.
For item 3 the starts_at from offer table is set to March, 01 2014 which is greater than NOW so (offer.starts_at IS NULL OR offer.starts_at <= NOW()) condition will skip the item 3 record
See fiddle demo

SQL query to get historic data from table

I have a table that stores the history of status changes to a person like this:
id | username | date | status
The date field is when the status was updated and the status field contains the new status that the person has since that date.
So the date in my table could be something like this:
1 | serafeim | 2012-03-03 | "NEW"
2 | john | 2012-03-05 | "NEW"
3 | serafeim | 2012-03-13 | "PENDING"
4 | serafeim | 2012-03-15 | "OLD"
5 | john | 2012-03-05 | "PENDING"
etc etc.
Now, I'd like to have a query that for a specific date in the past will retrieve the status that each user had then. For instance, for 2012-04-14 I'd like to get the following results
serafeim | "PENDING"
john | "NEW"
for 2012-03-04 I should get
serafeim | "NEW"
Can anybody think of an SQL query that will do that ? I don't want to do this programatically ! I'm using mysql but I don't think that that's relative to my problem...
Thanks in advance
The following query identifies the latest record for a given username, before the date specified, and joins the history table with that latest record ID to fetch the rest of the details.
SELECT a.*
FROM
history a
JOIN (SELECT username, MAX(id) 'id' FROM history
WHERE date < #inputDate
GROUP BY username
) as b
ON a.id = b.id
Get the first record of the user having the date less or equal to the input date:
declare #userid nvarchar(128)
declare #date datetime
SELECT userid, status
FROM
(
SELECT limit 1 * FROM mytable
WHERE date <= #date
AND userid = #userid
ORDER by date desc
)
Untested! And sorry if any syntax error.
I cannot test right now on a MySql database, but this query should do the job.
The table2 query retrieve the max date in which you registered an event for every user name before the desired date: this should be the last status event for that person.
The join get the status.
select username, status from table
join
(
select username, max(date) as maxdate from table
where date <= '2012-04-14'
group by username ) table2
on table.username = table2.username and table.date = table.2maxdate
Another way could be without join
select username, status from table
where date = (select max(date) as maxdate from table
where date <= '2012-04-14'
group by username )