Extract values of a sorted request with SQL - mysql

I have this sql table with people's name and ages.
Bob 28
Bryan 30
Jim 25
John 42
Bill 22
Sam 28
Tom 26
I would like to make a sql command to order all people by age desc, find a name in it, a return the preceding one, the founded and the next one with their position.
For example, admit that I would like to find Tom, my request should return :
Name Age Rank
Jim 25 2
Tom 26 3
Bob 28 4
Jim has the number 2 because Bill is the youngest
Is it possible to do something like this ?
Thanks in advance for any help

SQL isn't suited for row-based operations. There's no easy way to do "find a row where some condition(s) = true, then return the previous row" in a single query. You can do it in a couple steps, though:
a) Run one query to retrieve 'Tom' and his age (26).
b) Run another query to get the next older person
SELECT name, age FROM ... WHERE age > 26 ORDER BY age ASC LIMIT 1
c) Repeat but for next younger:
SELECT name, age FROM ... WHERE age < 26 ORDER BY age DESC LIMIT 1
This'll fetch people who are at least 1 year old/younger... You don't specify what happens if there's multiple people of the same age (e.g. There's Fred who's also 26, or Doug and Elmer who are both 25), so I'm ignoring those conditions.

Related

SQL QUERY to show records of names that are recorded several times and are unique towards each other

For example, let us consider this table:
In this image consists of rows of 8 where names like Mike,Glenn,Daryl,Shane and Patricia is included with their respective ages
Id
Name
Age
1
Mike
25
2
Glenn
19
3
Glenn
19
4
Daryl
56
5
Shane
30
6
Shane
30
7
Patricia
16
Now I want to insert the type of query that will show the names without repetitions like This, not like This
EDIT: I entered the data from first picture. The request is to list the names without duplicates, as shown in the second and third picture but I will not convert them to text.
DISTINCT specifies removal of duplicate rows from the result set.
SELECT DISTINCT Name
FROM tablename
see: use DISTINCT in SELECT
You can use GROUP BY to achieve it.
SELECT * FROM your_table
GROUP BY your_table.name
ORDER BY id
With the data you gave, the result from this query will be:
id
name
age
1
Mike
25
2
Glenn
19
4
Deryl
56
5
Shane
30
7
Patricia
16

Sql query to clean up junk record

Somehow a table is having junk data, need to clean it up and generate a new table.
I think it should use case or some row_number over, tried a few, failed.
Database is mysql.
original table:
Student Registration Course
John CS
John 2018
John 2017
Peter 2019 MATH
Mary 2016 MATH
Mary 2016 CS
The rule is, if we have duplicate records for a student, merge them together, for Registration, take max of year. If no any columns is missing, like Mary. order by Course asc, take first record. so the result will be :
Student Registration Course
John 2018 CS
Peter 2019 MATH
Mary 2016 CS
It looks like you want aggregation:
select student
, max(registration) as registration
, min(course) as course
from original
group
by student;
SELECT Student, MAX(Registration), MAX(Course)
-- or MIN(Course) if you want the first alphabetical
FROM YourTable
GROUP BY Student

Trouble with Group By and Having in SQL

I am trying to learn Group By and Having but I can't seem to understand what happened here. I used w3shools SQL Tryit Editor.
The table I created is:
name age country
------------------------
Sara 17 America
David 21 America
Jared 27 America
Jane 54 Canada
Rob 32 Canada
Matthew 62 Canada
The Query I used:
select
sum(age), country
from
NewTable
group by
country
having
age>25;
I expected the query to categorize the information by country and use age>25 filter to create the results but here is the output:
sum(age) country
--------------------
65 America
148 Canada
What happened?! The result is sum of American and Canadian people in all ages.
The piece you're missing is specific to the having keyword. Using the having clause in your query is applied to the dataset after the grouping occurs.
It sounds like you are expecting the records with age less than 25 to be excluded from your query before grouping occurs. But, the way it works is the having clause excludes the total age for each group that sums to a total over 25.
If you want to exclude individual records before totaling the sum of the age, you could do something like this (using a where clause which is applied prior to grouping):
select sum(age), country from NewTable where age > 25 group by country;
A where clause puts a condition on which rows participate in the results.
A having clause is like a where, but puts a condition on which grouped (or aggregated) values participate in the results.
Either, try this:
select sum(age), country
from NewTable
where age > 25 -- where puts condition on raw rows
group by country
or this:
select sum(age), country
from NewTable
group by country
having sum(age) > 25 -- having puts a condition on groups
depending on what you're trying to do.

how to find the lag of the first observation in a grouped data in SAS

I have a dataset,and it looks like this:
id name score
1 daniel 30
1 jane 20
1 keisha 70
2 kelly 30
2 jerry 60
2 jay 40
i want to find the difference between scores, comparing the FIRST score of each group to all other scores in that group. So for example,
i want to compare jane's score to daniel's (30-20=10) and compare keisha's score to daniel's (abs(30-70) = 40)
And to start over by comparing jerry's score to kelly's (abs(30-60=30)) and jay's score to kelly's (40-30 = 10).
id name score compare
1 daniel 30 .
1 jane 20 10
1 keisha 70 40
2 kelly 30 .
2 jerry 60 30
2 jay 40 10
Does anybody know of a way to write this in SAS? or any SQL command?
I've tried the following
data scoring_prep;
set scoring_prep;
by id;
if not missing(score) then do;
scorediff = abs(dif(score));
if id ne lag(id) then scorediff = .;
end;
run;
but this only find provides lag of the previous record, so keisha, for example, will be compared with jane instead of daniel.
Since you are not comparing to the previous value you do not want to use the LAG() or DIF() function. Instead use a retained variable to carry the comparison value forward.
data want;
set scoring_prep;
by id;
retain baseline;
if first.id then baseline=score;
else scorediff=abs(baseline - score);
run;

MySQL time between different rows as an aggregate

This is my first post, so please forgive me for any formatting errors.
I need to find the time between each row of data for a large number of users, and then take the average of those times as a function of time. I know how to to do the latter part, I just don't know how to find the time differences between rows.
For example, I can generate this output very easily (for all users sequentially, just using 'John' as an example):
User order time
John 13 2013-10-31 22:35:19
John 12 2013-10-18 23:16:50
John 11 2012-12-07 00:38:34
John 10 2012-06-10 02:19:14
John 9 2010-07-02 00:55:54
John 8 2009-12-20 23:43:41
John 7 2009-12-20 01:14:32
John 6 2009-12-18 15:12:40
John 5 2009-12-13 00:38:38
John 4 2009-12-12 16:00:13
John 3 2009-12-12 14:50:18
John 2 2009-12-11 18:41:15
John 1 2009-12-04 03:12:06
But, what I need to find out how to do is create this:
User order time timeDiff
John 13 2013-10-31 22:35:19 13
John 12 2013-10-18 23:16:50 300
John 11 2012-12-07 00:38:34 170
John 10 2012-06-10 02:19:14 ...
John 9 2010-07-02 00:55:54 ...
John 8 2009-12-20 23:43:41 ...
John 7 2009-12-20 01:14:32 ...
John 6 2009-12-18 15:12:40 ...
John 5 2009-12-13 00:38:38 ...
John 4 2009-12-12 16:00:13 ...
John 3 2009-12-12 14:50:18 ...
John 2 2009-12-11 18:41:15 ...
John 1 2009-12-04 03:12:06 NULL
This is easily done in excel, but I need the grouping properties for users + dates in order to do some analysis.
All help appreciated!!
Try this
Select current.time - previous.time As TimeDiff
From UserTable current Join
UserTable previous On current.User = pervious.User And current.Order - 1 = previous.Order
Where current.User = 'John'
That should get you the time differences.
You said you knew how to get the average from there.
Like you said, it's easy in Excel. Referring to relative rows in SQL is pretty hard, especially in MySQL.
You have to use server-side variables to establish "state", so you can remember previous values. e.g.
SET prev := null;
SELECT User, Order, time, IF(#prev IS NOT NULL, time - #prev, null), #prev := time;
It gets even harder since you're doing this for multiple users, and will have to add extra code to detect when the user changes.
For this kind of thing, you're better off doing the calcs in client-side code. Which means just do:
SELECT User, Order, UNIX_TIMESTAMP(time)
...
ORDER BY User, Order
and then you can feed those timestamps directly to PHP's time system for formatting/calculations.