MySQL select a row preceding the row with ID = X - sql

this is actually a sorting question almost solved thanks to other posts I've found here on StackOverflow.
basically i'm trying to bumb a row up , no need to insert it somewhere in the middle of the table and change all the following rows id's just one place up (replace order id's with previous).
I've found a solution for this in another post WHEN the id's are known for example.
ID | name | surname
1 | John | doe
2 | Jane | Dane
but my table has gaps and often its more like
ID | name | surname
2 | John | doe
7 | Jane | Dane
So i can't really rely on subtracting the id by 1 and replacing row 7 with the row number 7-1.
Is it somehow possible to swwitch the row id X with the one before without knowing the other id ?
is it possible to do it SQL only ? i have an idea of php-ing it but it seems alot like a fix not a solution.

This would work in SQL Server, I'm assuming in MySQL too:
SELECT *
FROM MyTable MT
...
WHERE ID = (SELECT MAX(ID) FROM MyTable MT2 WHERE ID < MT.ID)

Related

SQL - Merge duplicate rows by the most recent

I have a big SQL database with these tables for example:
first_name | last_name | email | country | created_at
-----------------------------------------------------------------
john | DOE | johndoe#email.com | USA | 2016-05-01
john | DOE | johndoe#email.com | FRANCE | 2019-05-03
doe | John | johndoe#email.com | CANADA | 2011-08-23
The previous database was built without a unique email (yes it's horrible).
So, I need to merge the user with same email but different data with the most recent record.
Then update the database by deleting the older one and keep the latest one.
Excuse me if it's not clear..
Something like this?
delete t
where t.created_at < (select max(t2. created_at)
from t t2
where t2.email = t.email
);
With EXISTS:
delete tablename t
where exists (
select 1 from tablename where email = t.email and created_at > t.created_at
)
EXISTS will return TRUE as soon as it finds 1 row with the same email and date greater than the current row, so it does not need to scan the whole table for every row.
You mentioned that this is a big database. I will then suggest that you add an index on the table before running the script by either #forpas or #Gordon Linoff as these scripts might take a long time to complete when dealing with millions of rows.
The index could be created like this:
CREATE INDEX tablename_index ON tablename (email, created_at);
And then afterwards, if you don't need the index any more, you can drop it like this:
DROP INDEX tablename_index ON tablename;

Novice seeking help, Max Aggregate not returning expected results

I'm still very new to MS-SQL. I have a simple table and query that that is getting the best of me. I know it will something fundamental I'm overlooking.
I've changed the field names but the idea is the same.
So the idea is that every time someone signs up they get a RegID, Name, and Team. The names are unique, so for below yes John changed teams. And that's my trouble.
Football Table
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 101 | Bill | Blue |
| 102 | Tom | Green |
| 103 | John | Green |
+------------+----------+---------+
With the query at the bottom using the Max_RegID, I was expecting to get back only one record.
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 103 | John | Green |
+------------+----------+---------+
Instead I get back below, Which seems to include Max_RegID but also for each team. What am I doing wrong?
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 103 | John | Green |
+------------+----------+---------+
My Query
SELECT
Max(Football.RegID) AS Max_RegID,
Football.Name,
Football.Team
FROM
Football
GROUP BY
Football.RegID,
Football.Name,
Football.Team
EDIT* Removed the WHERE statement
The reason you're getting the results that you are is because of the way you have your GROUP BY clause structured.
When you're using any aggregate function, MAX(X), SUM(X), COUNT(X), or what have you, you're telling the SQL engine that you want the aggregate value of column X for each unique combination of the columns listed in the GROUP BY clause.
In your query as written, you're grouping by all three of the columns in the table, telling the SQL engine that each tuple is unique. Therefore the query is returning ALL of the values, and you aren't actually getting the MAX of anything at all.
What you actually want in your results is the maximum RegID for each distinct value in the Name column and also the Team that goes along with that (RegID,Name) combination.
To accomplish that you need to find the MAX(ID) for each Name in an initial data set, and then use that list of RegIDs to add the values for Name and Team in a secondary data set.
Caveat (per comments from #HABO): This is premised on the assumption that RegID is a unique number (an IDENTITY column, value from a SEQUENCE, or something of that sort). If there are duplicate values, this will fail.
The most straight forward way to accomplish that is with a sub-query. The sub-query below gets your unique RegIDs, then joins to the original table to add the other values.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, gets the list of IDs
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
GROUP BY
f2.Name
) AS sq
ON
sq.Max_RegID = f.RegID;
EDIT: Sorry. I just re-read the question. To get just the single record for the MAX(RegID), just take the GROUP BY out of the sub-query, and you'll just get the current maximum value, which you can use to find the values in the rest of the columns.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, now gets the MAX ID
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
) AS sq
ON
sq.Max_RegID = f.RegID;
Use row_number()
select * from
(SELECT
Football.RegID AS Max_RegID,
Football.Name,
Football.Team, row_number() over(partition by name order by Football.RegID desc) as rn
FROM
Football
WHERE
Football.Name = 'John')a
where rn=1
simply you can edit your query below way
SELECT *
FROM
Football f
WHERE
f.Name = 'John' and
Max_RegID = (SELECT Max(Football.Max_RegID) where Football.Name = 'John'
)
or
if sql server simply use this
select top 1 * from Football f
where f.Name = 'John'
order by Max_RegID desc
or
if mysql then
select * from Football f
where f.Name = 'John'
order by Max_RegID desc
Limit 1
You need self join :
select f1.*
from Football f inner join
Football f1
on f1.name = f.name
where f.Max_RegID = 103;
After re-visit question, the sample data suggests me subquery :
select f.*
from Football f
where name = (select top (1) f1.name
from Football f1
order by f1.Max_RegID desc
);

Return only first row with particular value in a column

I realize that this has probably been asked a billion times, and I could swear I've done this in the past, but tonight I've got brain block or something and can't figure it out...
I have a database table ("t1") where I need to be able to retrieve only the first row where a particular value appears in a particular column.
Here's a sample of the data:
id | qID | Name
---------------------
1 | 1 | Bob
2 | 3 | Fred
3 | 1 | George
4 | 1 | Jack
What I want as a result is:
id | qID | Name
---------------------
1 | 1 | Bob
2 | 3 | Fred
The only column I actually need to get out of the query is the first one, but that's not where the duplicates need to be eliminated, and I thought it might be confusing not to show the entire row.
I've tried using this:
select id, qID, ROW_NUMBER() over(partition by qID order by qID) as zxy
from t1 where zxy = 1
But it gives me this error:
Msg 207, Level 16, State 1, Line 14
Invalid column name 'zxy'.
If I remove the where part of the query, the rest of it works fine. I've tried different variable names, using single or double quotes around 'zxy' but it seems to make no difference. And try as I might, I can't find the part of the SQL Server documentation where it discusses assigning a variable name to an expression, as in the "as zxy" part of the above query... if anybody has a link for that, that's quite useful.
Needless to say, I've tried other variable names besides "zxy" but that makes no difference.
Help!
WHERE clause is applied earlier in the process than SELECT. Therefore the calculated column zxy is not available in WHERE. In order to achieve your goal you need to put your original query in a subquery or CTE.
select id, qid
from
(
select id, qID, ROW_NUMBER() over(partition by qID order by qID) as zxy
from t1
) q
where zxy = 1
Output:
| id | qid |
|----|-----|
| 1 | 1 |
| 2 | 3 |
Here is SQLFiddle demo
Logical Processing Order of the SELECT statement
1 FROM
2 ON
3 JOIN
4 WHERE
5 GROUP BY
6 WITH CUBE or WITH ROLLUP
7 HAVING
8 SELECT
9 DISTINCT
10 ORDER BY
11 TOP
Where Clause Execute Before Select Clause so You can not find ZXY in Where cluase
with cte as
(
select id, qID, ROW_NUMBER() over(partition by qID order by qID) as zxy
from t1
)
select * from cte where zxy = 1
Here is my blog it might help you http://sqlearth.blogspot.in/2015/05/how-sql-select-statement-logically-works.html

How to join (without concatenating) two result sets of two fields together as one long ‘list’

What SQL Code do I require to perform the following? :
I have one table (lets arbitrarily call the table 'Names'):
ID | Name1 | Name2
---+--------+-------
1 | Fred | Jack
2 | Jack | Jim
3 | Jill | Fred
4 | Jim | Jack
etc
What I'd like is to produce is a single list of Name1 and Name2 (I don't even care about Grouping or Ordering) as such, but I would like to keep the original 'ID' association with the name:
ID | Names
---+------
1 | Fred
1 | Jack
2 | Jill
2 | Jim
3 | Jack
3 | Jim
4 | Fred
4 | Jack
Why do I want to do this? Because it looks easy and as a SQL coder I should probably be able to perform this task, but I can't figure out a solution that will create this output. Further more I've only manage to find people with the desire to concatenate the fields, which is a simple task, but I'm not interested in concatenation.
Additional Question: Would the SQL query be vastly different if Name1 field was in a different table to Name2? (if it is different, what would it look like?)
Additional Question: Would the SQL query be simpler if we didn't care about the ID field? If so, what would that look like.
You can use this form to include the id, and give you a specific ordering by ID.
SELECT n.id, n.name1 FROM names n
UNION
SELECT m.id, m.name2 from names m
ORDER BY id ASC;
If it were in a different table, the use of UNION doesn't have to change, since we're bringing the results from the table together, and ordering them by ID. This doesn't mean that the data is related, though.
SELECT n.id, n.name FROM name_one n
UNION
SELECT m.id, m.name from name_two m
ORDER BY id ASC;
If we didn't care about the ID field, it would be ever so lightly simpler - it's just selecting one column at that point.
SELECT ID, Name1 as Names FROM person
UNION
SELECT ID, Name2 as Names FROM person
?
. . What you mean by "without concatenating"?

Select ID given the list of members

I have a table for the link/relationship between two other tables, a table of customers and a table of groups. a group is made up of one or more customers. The link table is like
APP_ID | GROUP_ID | CUSTOMER_ID
1 | 1 | 123
1 | 1 | 124
1 | 1 | 125
1 | 2 | 123
1 | 2 | 125
2 | 3 | 123
3 | 1 | 123
3 | 1 | 124
3 | 1 | 125
I now have a need, given a list of customer IDs to be able to get the group ID for that list of customer IDs. Group ID may not be unique, the same group ID will contain the same list of customer IDs but this group may exist in more than one app_id.
I'm thinking that
SELECT APP_ID, GROUP_ID, COUNT(CUSTOMER_ID) AS COUNT
FROM GROUP_CUST_REL
WHERE CUSTOMER_ID IN ( <list of ids> )
GROUP BY APP_ID, GROUP_ID
HAVING COUNT(CUSTOMER_ID) = <number of ids in list>
will return me all of the group IDs that contain all of the customer ids in the given list and only those group ids. So for a list of (123,125) only group id 2 would be returned from the above example
I will then have to link with the app table to use its created timestamp to identify the most recent application that the group existed in so that I can then pull the correct/most up to date info from the group table.
Does anyone have any thoughts on whether this is the most efficient way to do this? If there is another quicker/cleaner way I'd appreciate your thoughts.
This smells like a division:
Division sample
Other related stack overflow question
Taking a look at the provided links you'll see the solution to similar issues from relational alegebra's point of view, doesn't seem to be quicker and arguably cleaner.
I didn't look at your solution at first, and when I solved this I turned out to have solved this the same way you did.
Actually, I thought this:
<number of ids in list>
Could be turned into something like this (so that you don't need the extra parameter):
select count(*) from (<list of ids>) as t
But clearly, I was wrong. I'd stay with your current solution if I were you.