I am working adjacent id exchange problem. I found one solution, it partly works.
SELECT tmp.id, tmp.student FROM
(
SELECT id-1 AS id, student FROM seat WHERE id%2 = 0 -- even id -1
UNION
SELECT id+1 AS id, student FROM seat WHERE id%2 = 1 -- odd id +1
) tmp
ORDER BY tmp.id
I know the basic idea, but still confuse to the syntax or expression. I am wondering where the tmp comes from? Is the other way to use Alias in SQL?
IN the sample the tmp alias is changed in my_table_alias
SELECT my_table_alias.id, my_table_alias.student FROM
(
SELECT id-1 AS id, student FROM seat WHERE id%2 = 0 -- even id -1
UNION
SELECT id+1 AS id, student FROM seat WHERE id%2 = 1 -- odd id +1
) my_table_alias
ORDER BY my_table_alias.id
This is introducued by the select ,,,, FROM ( subquery ) my_table_alias sintax
when you use a FROM( subquery ) you need a my_table_alias ..
The table alias for then FROM(subquery) in mandatory If you want refer to the column coming from the FROM(subquery) in outer part of query
You can just use a case expression:
SELECT (CASE WHEN s.id % 2 = 0 THEN s.id - 1 ELSE s.id + 1
END) AS id,
s.student
FROM seat s;
UNION is definitely not the right way to approach this problem.
Note that the s plays the same role as tmp in your question. It is a table alias that names a table or subquery in the from clause.
Related
Currently, I have a task: for the presented table, to change students' order by replacing odd and even sequence (example below). There is only one condition, if the number of students is odd, that last number should not be changed. I currently wrote a code like this, however, for me, it seems pretty clumsy. How different and more efficient should this code be written?
CREATE TABLE student (id int, name varchar(128));
INSERT INTO student (id,name) VALUES
(1,'Aurimas'),
(2,'Darius'),
(3,'Eligijus'),
(4,'Giedrius'),
(5,'Justinas');
SELECT CASE
WHEN mod((SELECT id FROM student ORDER BY id DESC LIMIT 1),2) = 0 THEN
CASE
WHEN mod(id, 2) = 0 THEN id-1
ELSE id=id+1
END
ELSE
CASE
WHEN mod(id, 2) = 0 AND id <> (SELECT id FROM student ORDER BY id DESC LIMIT 1) THEN id-1
WHEN mod(id, 2) = 1 AND id <> (SELECT id FROM student ORDER BY id DESC LIMIT 1) THEN id+1
ELSE id
END
END AS new_id, name
FROM student
ORDER BY new_id ASC;
I have this:
id name
1 Aurimas
2 Darius
3 Eligijus
4 Giedrius
5 Justinas
And it should look like this:
id name
1 Darius
2 Aurimas
3 Giedrius
4 Eligijus
5 Justinas
You could group your ids by the value of (id+1) DIV 2. Then all you have to do is swap the minimum id with the maximum id in each pair of rows and make sure you handle the case when there are odd number of rows properly.
Something like this:
set #maxId = (select max(id) from student);
select
if (
#maxId = id and mod(id, 2) != 0,
id,
if (
mod((id+1), 2) = 0,
id+1,
id-1
)
) as id,
name
from student
order by id;
db<>fiddle
I'm not sure how you're defining 'efficient', but how about...
select ROW_NUMBER() OVER (
ORDER BY ceiling(id/2), id desc
) new_id,name from student
The ROW_NUMBER function has been supported in MySQL since version 8.0.
EDIT: I suppose something like will satisfy the quibblers...
WITH cte (id,n) AS (SELECT id, ROW_NUMBER() OVER (ORDER BY id) n FROM student)
SELECT x.name
, ROW_NUMBER() OVER (ORDER BY CEILING(cte.n/2), x.id desc) new_id
FROM student x
JOIN cte
ON cte.id = x.id;
I would recommend to use the below query because
it has many advantages here I am calculating only one max id select but in your case for every case, one select will execute this will reduce that time and only one max id will be used for the odd case
your odd and even operation is the same but in odd you are skipping last record so I added that condition
SET #MAXID = (SELECT MAX(ID) FROM STUDENT);
SELECT
CASE
WHEN MOD(#MAXID,2) != 0 AND ID=#MAXID THEN ID
ELSE CASE WHEN MOD(ID, 2) = 0 THEN ID-1 ELSE ID+1 END
END AS NEW_ID, NAME
FROM STACK_USER.STUDENT
ORDER BY NEW_ID ASC;
I think this would be useful in your case I have checked every condition in my local it will work properly
Screen Shot For Better Understanding:
I need to make a request in SQL.
I have a field that contains IDs.
These IDs are written in 2 ways and are prefixed either by'C0' or by'E0' for example: "C0121213" or "E0121213".
I would like to make a query allowing me to find the number of IDs starting with C0 but not duplicating starting with E0.
That is, I would like to find IDs that do not have C0 or E0 pairs.
Thank you in advance
I started with a request :
SELECT *
FROM SBYN
WHERE ID IN (
SELECT LID
FROM SBYN
WHERE LEFT(ID,2) = 'C0'
OR LEFT(ID, 2) = 'E0'
GROUP BY LID HAVING COUNT(*) > 1
)
ORDER BY ID
NOT EXISTS comes to mind:
SELECT COUNT(*)
FROM SBYN s
WHERE s.ID LIKE 'C0%' AND
NOT EXISTS (SELECT 1
FROM SBYN s2
WHERE s2.ID LIKE 'E0%' AND
SUBSTRING(s2.ID, 2) = SUBSTRING(s.ID, 2)
);
If you want the IDs, then use SELECT ID rather than SELECT COUNT(*).
Using EXISTS:
SELECT
ID
FROM SBYN s1
WHERE
ID LIKE 'C0%' AND
NOT EXISTS (SELECT 1 FROM SBYN s2
WHERE s2.ID LIKE 'E0' AND
SUBSTRING(s1.ID, 3) = SUBSTRING(s2.ID, 3));
With NOT EXISTS:
select * from sbyn s
where not exists (
select 1 from sbyn
where left(id, 2) <> left(s.id, 2) and
right(id, 3, length(id)) = right(s.id, 3, length(s.id))
)
This will return all the non duplicates.
If you care only about those starting with C0 add to the where clause:
and left(s.id) = 'C0'
I'm trying to include select statement in the then of case statement but the output is not as expected. I know there is different method to do this but can it be done the way i'm trying to do.
Using the following example data:
create table example(name varchar(10));
insert into example values
('abc'),('bcd'),('xyz');
I have tried this query (here is the fiddle):
select
case when ((select * from example where name='abc')>=1)
then (select * from example where name='abc')
else (select count(*) from example)
end
from example
But it outputs
3
3
3
Expected output if name='abc' exist
name
abc
if not the count(*)
Thanks in advance
Your subquery in the example is (select * from example where name='abc') which is a result set, not a scalar value. Currently it "works" because it is comparing the only column in the table to the value 1 but if you had more than one column in the table it would error out. Perhaps you intended (select count(*) from example where name='abc')?
Similarly, the THEN clause in a case can only be used to provide a single column value. In order to do this, perhaps you meant the following:
select
case when exists (select * from example where name='abc')
then (select name from example where name='abc')
else (select count(*) from example)
end
from example
But even here you will get three rows and there is no correlation between the rows in example and the result set, so I am not really sure what you're trying to do. I imagine there is a higher purpose though so I will leave it at that.
This should do the trick
select distinct
case when ((select count(name) from example where name='abc')>=1)
then (select * from example where name='abc')
else (select count(*) from example)
end
from example
Let me know if it works.
Point 1:
For the query, you are trying, the from example in the last will cause to loop through all the records and fetch all the records. To restrict that, you have to remove that.
Point 2:
You can't combine multi row select * in a true condition with a single row count(*) in a false condition. You should limit to select a single row.
Example:
select
case when ( select count(*) from example where name='abc' ) >= 1
then ( select * from example where name='abc' limit 1 )
else ( select count(*) from example )
end as name
No need to bother with the complex queries.
SELECT COUNT(*) AS ct
FROM example
GROUP BY name = 'abc'
ORDER BY name = 'abc' DESC
LIMIT 1;
If you really want to use CASE just for the sake of using it:
SELECT
CASE name
WHEN 'abc' THEN 'abc'
ELSE 'others'
END AS name, COUNT(*) AS ct
FROM example
GROUP BY name = 'abc'
ORDER BY name = 'abc' DESC
LIMIT 1;
Try below query, which will work even you enter a second duplicate row as value 'abc'. Mostly above suggested queries will not work as you enter this duplicate row while as per your query condition (>=1), there can be multiple rows for name as 'abc'.
SELECT
CASE WHEN b.cnt>=1
THEN a.name
ELSE (SELECT COUNT(*) FROM EXAMPLE)
END
FROM (SELECT DISTINCT NAME FROM EXAMPLE WHERE NAME='abc') a
JOIN (SELECT NAME,COUNT(*) AS cnt FROM EXAMPLE WHERE NAME='abc') b
ON a.name=b.name
I have a table :
ID | time
1 | 300
1 | 100
1 | 200
2 | 200
2 | 500
I want to get 2nd row for every ID
I know that I can get 1st row as
select ID,time from T group by ID;
But I don't know about how to get 2nd row for every ID.
I know about limit and offset clause in mysql, but can't figure out how to use them here.
How can I do it ?
EDIT : Actually, time is not ordered. I forgot to specify that. I have made an edit in the table.
i have just an idee how to make it but i couldnt fix it , maybe you can fix it. any suggest is appreciated to correct my query
first this to select the first row of each id.
SELECT min(id) id
FROM TableName t2
group by id
then select the min(id) which are not in the first query to select to min(id) (which is second row)
like that
SELECT min(id) id ,time
FROM TableName
WHERE id NOT IN (
SELECT min(id) id
FROM TableName
GROUP BY id
)
GROUP BY id
** as i said its just suggest . it returns me 0 values.if u fix it let me edit my post to be helpful
here a demo
SELECT ID, MAX(time) time
FROM
(
select ID, Time
from TableName a
where
(
select count(*)
from TableName as f
where f.ID = a.ID and f.time <= a.time
) <= 2
) s
GROUP BY ID
SQLFiddle Demo
SELECT x.*
FROM test x
JOIN test y
ON y.id = x.id
AND y.time >= x.time
GROUP
BY id,time
HAVING COUNT(*) = n;
Note that any entries with less than n results will be omitted
You cannot do this with the tables that you have. You could make a valiant attempt with:
select id, time
from (select id, time
from t
group by t
) t
where not exists (select 1 from t t2 where t2.id = t.id and t2.time = t.time)
group by id
That is, attempt to filter out the first row.
The reason this is not possible is because tables are inherently unordered, so there is not real definition of "second" in your tables. This gives the SQL engine the opportunity to rearrange the rows as it sees fit during processing -- which can result in great performance gains.
Even the construct that you are using:
select id, time
from t
group by id
is not guaranteed to return time from the first row. This is a (mis)feature of MySQL called Hidden Columns. It is really only intended for the case where all the values are the same. I will admit that in practice it seems to get the value from the first row, but you cannot guarantee that.
Probably your best solution is to select the data into a new table that has an auto-incrementing column:
create table newtable (
autoid int auto_increment,
id int,
time int
);
insert into newtable(id, time)
select id, time from t;
In practice, this will probably keep the same order as the original table, and you can then use the autoid to get the second row. I want to emphasize, though, the "in practice". There is no guarantee that the values are in the correct order, but they probably will be.
DECLARE topScorer INT default 0;
SELECT id INTO topScorer FROM game_player
WHERE game_player.score = (SELECT max(score) FROM game_player)
A bad example but one that could easily result from naive coding... it doesn't work in my testing if multiple rows are returned, how can I get the first returned row into the variable?
Do you need just the one score?
SELECT id
INTO topScorer
FROM game_player
WHERE game_player.score = ( SELECT max(score) as maxScore
FROM game_player
) LIMIT 1
Update:
Sir Rufo was right, the code above has now been corrected.
Apply limit in sub query to get only 1 value from sub query
SELECT id
INTO topScorer
FROM game_player
WHERE game_player.score = ( SELECT max(score)
FROM game_player LIMIT 1 );
Or to get multiple value from sub query used below one:
SELECT id
INTO topScorer
FROM game_player
WHERE game_player.score in ( SELECT max(score)
FROM game_player );
Use LIMIT x to ensure you are receiving only x rows from your query.
In this case you only want to get 1 row:
SELECT id
INTO topScorer
FROM game_player
WHERE game_player.score = ( SELECT max(score)
FROM game_player )
LIMIT 1
SQL Fiddle DEMO
As a working alternative you can also use this
SELECT id
INTO topScorer
FROM game_player
ORDER BY score DESC
LIMIT 1
SQL Fiddle DEMO
1) declare variable in SP:
declare #CourseID int
set #CourseID = 0
2) We need two query first for assign ID to variable and inner query for select only Top 1 record form table. In where clause of first query we compare ID with result of inner query:
SELECT #CourseID = ID FROM Course ID = ( Select Top 1 ID from Course )
3) Now check Variable value:
if(#CourseID > 0 )
Begin
//This mean ID of first row is assigned to CourseID
End
Else
Begin
//Can't found any record.
End