I'm still learning SQL so I was wondering if there is a better way of doing the following.
I need to get row data for the lowest and highest values in a column (lets call it columnA).
I would use:
SELECT *
FROM table
ORDER BY columnA
DESC LIMIT 1
Problem is I get only one result due to the LIMIT 1 but there may be identical lowest / highest values in ColumnA that have different values in the other columns. I need those other rows too.
There is SELECT(MAX) but I believe that will also only produce one row of data.
The ways I can think do this are by putting the highest / lowest columnA values into a variable and then back into a second query OR use a LEFT JOIN on alias tables to do this in single query but is there any more direct method?
The simplest way is to perform a sub-query:
SELECT * FROM MyTable WHERE columnA = (SELECT MAX(columnA) FROM MyTable);
You can even query both extremes at once:
SELECT * FROM MyTable
WHERE columnA = (SELECT MAX(columnA) FROM MyTable);
OR columnA = (SELECT MIN(columnA) FROM MyTable);
I haven't tested the next one (don't know if MySQL supports UNION in
sub-queries), but it should work as well, might be a bit more
efficient (depending on your data size).
SELECT * FROM MyTable
WHERE columnA IN (
SELECT MAX(columnA) FROM MyTable
UNION
SELECT MIN(columnA) FROM MyTable
);
Another option is :
SELECT *
FROM MyTable m1
WHERE not exists (select 1 from MyTable where columnA > m1.columnA)
Related
In MySql I have a complex SELECT which returns a table of about 500k rows and without primary key.
I would like to select 10 rows based on the different position they have in the resulting table, as efficiently as possible.
I tried using LIMIT:
SELECT * FROM table_name LIMIT 5,1;
efficient but select only one row.
I tried with UNION and LIMIT, but I have to do the complex SELECT several times:
(SELECT * FROM table_name LIMIT 5,1)
UNION
(SELECT * FROM table_name LIMIT 1000,1)
UNION
(SELECT * FROM table_name LIMIT 50000,1)
...
Is there a more efficient way to get what I want?
In order to get the positions you must sort the data; there is no way around that. So, number your rows by the desired order and pick the positions you want:
select *
from
(
select t.*, row_number() over (order by ...) as rn
from table_name t
) numbered
where rn in (6, 1001, 50001);
Id like to run a query only if a value in the last row is correct. In my exemple if the value in ColumnA is 1 on the last row then i want to run MyQuery. But if the value is not 1 stop there and do nothing.
i've try with case and count(*) and also with If exists. but i keep getting error
SELECT CASE WHEN ((SELECT COUNT(*) FROM
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
THEN (MyQuery)
END
i've also try with if exists but it doesn'work either
if exists Select b.* from (Select a.* from table as a order by a.index desc limit 1) where b.ColumnA = 1
begin
(MyQuery)
end
can you point me what wrong in those query or maybee there's a better way to achive this.
EDIT. This query will be run on a trigger after each insert in that table the goal is to avoid running MyQuery on row that dont required it. MyQuery is a bit slow and most row dont required it to run.
I think we can rephrase your logic here to make it work as you want:
WITH cte AS (
SELECT ColumnA, ROW_NUMBER() OVER (ORDER BY index DESC) rn
FROM yourTable
)
(your query here)
WHERE (SELECT ColumnA FROM cte WHERE rn = 1) = 1;
The WHERE clause above would return either true or false, and would apply to all records in the potential result set from your query. That is, if the ColumnA value from the "last" record were 1, then you would get back the entire result set, otherwise it would be empty set.
Assuming your version of MariaDB supports neither ROW_NUMBER nor CTEs, then use:
(your query here)
WHERE (SELECT ColumnA FROM yourTable ORDER BY index DESC LIMIT 1) = 1;
It depends on what your query is.
INSERT ...
SELECT ... WHERE ... -- this could lead to zero rows being inserted
DELETE ...
WHERE NOT EXISTS ( SELECT ... ) -- this could lead to zero rows being deleted
UPDATE t1 JOIN t2 ... -- the JOIN may cause no rows to be updated
Note:
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
can be simplified (and sped up) to
( ( SELECT ColumnA FROM table ORDER BY index DESC LIMIT 1 ) = 1 )
Note that that is a true/false "expression", so it can be used in various places.
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.
I just need to know if a query returns or not a record.
Of course I can do this:
SELECT COUNT(*) FROM tbl WHERE conds;
But this returns the exact number of rows (of course), and I don't need this overhead.
So I thought this query:
SELECT COUNT(*) FROM (SELECT id FROM tbl WHERE conds LIMIT 1) as t1
Limiting the internal query to 1.
Is this faster? Or considering I am doing a subquery it cancels the benefits of LIMIT 1?
Note: for everyone asking theirself, I can't apply LIMIT 1 to the first query because it doens't work
The inner-select in the second query is redundant.
If you just want to check at-least of one row :-
SELECT 1 FROM tbl // return 1
WHERE conds // depends on your index and query
ORDER BY NULL // avoid file-sort
LIMIT 1; // minimum row
Why not just:
SELECT 1 FROM tbl WHERE conds LIMIT 1
You could do:
SELECT 1 WHERE EXISTS(SELECT id FROM tbl WHERE CONDITION)
Or something like:
SELECT 1 WHERE EXISTS (SELECT id FROM tbl WHERE id IN( 1000, 1001))