MYSQL top N rows from multiple table join - sql

Like, there is top keyword in sql server 2005, how to select top 1 row in mysql if i have join on multiple table & want to retrieve extreme of each ID/column. Limit restricts the no. of row returns so it can't solve my problem.

SELECT v.*
FROM document d
OUTER APPLY
(
SELECT TOP 1 *
FROM version v
WHERE v.document = d.id
ORDER BY
v.revision DESC
) v
or
SELECT v.*
FROM document d
LEFT JOIN
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY v.id ORDER BY revision DESC)
FROM version
) v
ON v.document = d.id
AND v.rn = 1
The latter is more efficient if your documents usually have few revisions and you need to select all or almost all documents; the former is more efficient if the documents have many revisions or you need to select just a small subset of documents.
Update:
Sorry, didn't notice the question is about MySQL.
In MySQL, you do it this way:
SELECT *
FROM document d
LEFT JOIN
version v
ON v.id =
(
SELECT id
FROM version vi
WHERE vi.document = d.document
ORDER BY
vi.document DESC, vi.revision DESC, vi.id DESC
LIMIT 1
)
Create a composite index on version (document, revision, id) for this to work fast.

If I understand you correctly, top doesn't solve your problem either. top is exactly equivalent to limit. What you are looking for is aggregate functions, like max() or min() if you want the extremes. for example:
select link_id, max(column_a), min(column_b) from table_a a, table_b b
where a.link_id = b.link_id group by link_id

Related

look up dynamic value from range in another table

I have 2 tables. The first one is a detail table with years and actual limit values. The second table has max control limits but only for certain years.
Table 1 & 2
What I want to do is list all of the detail records but pull in the values of the control limits if the year is less than the next one listed in the control table.
Desired results:
results
I have tried this query but it duplicates 2015, 2016 and 2017.
SELECT d.id, d.yeard, d.value, c.Column1
FROM detailTbl d
RIGHT OUTER JOIN controlTbl c ON d.dated <= c.datec
You may use Row_Number() function as the following to remove duplicates:
with cte as
(
Select D.id,D.yeard,D.val, C.limitVal,
row_number() over (partition by D.id order by C.yeard desc) as rn from
detailTbl D left join controlTbl C
on D.yeard>=C.yeard
)
Select B.id,B.yeard,B.val,B.limitVal from
cte B where B.rn=1 order by B.id
See a demo on MySQL 8.0 from here.

SQLite Random sub query result

I have 2 SQLite tables, table_a and table_b. Table A contains a list of categories and Table B contains a list of words.
I'm trying to get all categories from table_a and a random word from table_b for each category. The below query allows me to get 1 result per category that has the lowest id in table_b.
SELECT table_a.category_id, table_a.category, subQuery.word, subQuery.word_category, subQuery.word_id
FROM table_a,
(SELECT *
FROM table_b
GROUP BY category_id
HAVING MIN(word_id)
) subQuery
WHERE subQuery.category_id = table_a.category_id;
Is there a way to return 1 random result from table_b for each category in table_a instead of the value with the lowest id?
I'm not really having much luck finding an answer online. Any help would be appreciated.
You can use a window function to pick one row per category:
select
c.category_id, c.category,
w.word, w.word_category, w.word_id
from categories c
left join
(
select
word, word_category, word_id, category_id,
row_number() over (partition by category_id order by random()) as rn
from words
) w on w.category_id = c.category_id and w.rn = 1
order by c.category_id;
Demo: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=bbbe8541adf2d4f62883b2a1c36aaa41
Update
You say that the query does not work in SQLite 3.8. We can only suspect a bug here, because we see it works in SQLite 3.27.
Here is another approach that may work for you. I am selecting a random word ID with each category using a LIMIT subquery. Then I join the word data.
select c.category_id, c.category, w.word, w.word_category, w.word_id
from
(
select
c.category_id, c.category,
(
select word_id
from words w
where w.category_id = c.category_id
order by random()
limit 1
) as word_id
from categories c
) c
join words w on w.word_id = c.word_id
order by c.category_id;
Demo: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=7630068aa3ba71e70b7a6bddd83047dc

Can we select first row of data from column in sql?

I have a table with multiple data for same ID. I want to get the first row data for the ID.
I have added the below SQL that I have tried.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
LEFT OUTER JOIN "client_details" ON ("client"."id" = "client_details"."client_id")
Since I have multiple address for the same ID, can we get only the first id?
Currently the output I get is 2 rows with different addresses.
You can add to your SQL LIMIT 1 and in case you want to be sure the order you can also add to your SQL ORDER BY...
You can use distinct on:
select distinct on (c.id) c.id, c.company_name, cd.address
from Client c left join
client_details cd
on c.id = cd.client_id
order by c.id, ?;
The ? is for the column that specifies the ordering (the definition of "first"). I am guessing that cd.id is what you want.
Note that this query removes the double quotes and introduces table aliases. This is easier on both the eyes (to read) and the fingers (to type).
use row_number()
select * from
(
SELECT
"client"."id",
"client"."company_name",
"client_details"."address",row_number() over(partition by "client"."id" order by "client_details"."address") as rn
from Client
LEFT OUTER JOIN "client_details" ON "client"."id" = "client_details"."client_id"
)A where rn=1
If there is a field you can order the results by you could use a lateral join e.g.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
left join lateral (
select *
from client_details cd
where cd.client_id = client.id
order by [some_ordering_field]
limit 1
) "client_details" on true

correct select statement for multiple tables

I'm trying to get the latest 5 titles from 3 tables, I think I might be going wrong somewhere in my statement.
Here is my statement
SELECT * FROM news N, blog B, comments C WHERE active='1' ASC LIMIT 5
After FROM you can only set 1 table where the results will come from. Moreover if you use keyword ASC you should also say the field you order by try:
SELECT * FROM news WHERE active='1' order by 'fieldnew' ASC
UNION ALL
(SELECT * FROM blog WHERE active='1' order by 'fieldblog' ASC)
UNION ALL
(SELECT * FROM comments WHERE active='1' order by 'fieldcommnet' ASC)
You need to make a join on three tables provided !
in sql-server:
select top 5 * from
(
select * from News N inner join blog B on N.id=B.id inner join comments C on C.id=N.id
)x
order <something>

oracle select from 2 table restrict from 1 table and order by another

I have book and recipient table. I want to select maximum 20 rows order by recipient table's membershipdate column. After I got it, i want to order it by book table's id column. I wrote that sql. Is there any way to do this with less code?
SELECT *
FROM ( SELECT *
FROM ( SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
)
ORDER BY ID DESC
You can remove one layer of select:
SELECT *
FROM (
SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
ORDER BY id DESC;
Selecting b.* isn't normally a good, idea, it's better to specify the columns you actually want, even if you do really want them all - to make sure you get them in the order you expect.
You can also look at the row_number() analytic function in place of rownum, but that will give you slightly more code - not that it should matter, the effectiveness and efficiency of the query it rather more important that its length.
Doesn't this work?
SELECT * FROM
(SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115 ORDER BY r.membershipdate DESC
) WHERE ROWNUM <= 20
ORDER BY ID DESC
In oracle you can use ROW_NUMBER() function to reduce the code as you want -
and you can even mix your Order columns also.
select * from (
select b.*,row_number() over (order by r.membershipdate desc) cnt
from book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
order by cnt,b.id desc
) where cnt<=20;