How can I get latest comment from specific new? - sql

I have a table
News(newsId, text, date)
and
Comments(commentId, text, date, newsId)
and I need to select 10 newest news by date with a newest comment from each. So far I have this, how do I improve it and finish it?
SELECT date,
newsId,
commentid,
date
FROM News,
comments
ORDER BY date DESC
LIMIT 10;

You can do this using row_number() as:
select n.newsid, n.text, n.text, n.date, c.text as comment
from news n left join
(select c.*,
row_number() over (partition by c.newsid order by date desc) as seqnum
from comments c
) c
on c.newsId = n.newsId and seqnum = 1
order by n.date desc
limit 10;
Note that this returns news items with no comments. If you do not want those, then change the left join to an inner join.

if your dbms support row_number then you can try like below
with cte as
(select *,row_number()over(partition by newsId order by date desc) rn
from News
), cte1 as
(
select newsId, text, date from cte where rn<=10
) select cte1.*,c.commentId,c.text as commnt from cte1 join Comments c on cte1.newsid=c.newsid

For older DBMS subquery will useful :
select n.newsid, n.text, n.text, n.date, c.text as comment
from news n inner join
comments c
on c.newsId = n.newsId
where c.date in (select c1.date
from comments c1
where c1.newsid = n.newsid
order by c1.date desc
limit 10
);
Howver, for newest version DBMS row_number() is a generic to do this.

Related

SQL where in sql subquery with order by

This is my query
SELECT * FROM Place WHERE Place.Id IN (
SELECT TOP 10 PlaceId from #ResultPlaceList order by CPlaceId desc)
my result is ordered by Place.ID, but i want to have a result with ordered CPlaceId.
You could use a join to use the cPlaceId column for ordering:
select p.*
from Place p
join (
select top 10 PlaceId,
CPlaceId
from #ResultPlaceList
order by CPlaceId desc
) r on p.Id = r.PlaceId
order by r.CPlaceId;
You will have to do a JOIN to accomplish this. It will look something like this:
SELECT TOP 10 Place.*
FROM Place
JOIN #ResultPlaceList ON #ResultPlaceList.PlaceId = Place.Id
ORDER BY CPlaceId DESC
Something like this....
SELECT * FROM PLACE WHERE PLACE.ID IN (
SELECT PLACEID
FROM (
SELECT PLACEID
, ROW_NUMBER() OVER (ORDER BY CPLACEID DESC) RNUM
FROM #RESULTPLACELIST
) TMP
WHERE RNUM <= 10
)
'SELECT C.ID
FROM CustomerDetails C
LEFT JOIN UserInfo U ON C.ID = U.ID
Order by U.ID'
Can you modify your query like this

How can I fix my GROUP BY clause

I have 2 tables as seen below:
Now the question is :
How can I have a view which shows the details of the last Owner? in other words I need the details of person who has MAX(StartDate) in tbl_Owners table?
I want to find the latest owner of each apartment.
I tried different approaches but I couldn't find the way to do that.
I know I need to get the personID in a Group By clause which groups records by AppID but I can't do that
Thank you
Try this
select t1.* from tbl_persons as t1 inner join
(
select t1.* from tbl_owners as t1 inner join
(
select appid,max(startdate) as startdate from tbl_owners group by appid
) as t2
on t1.appid=t2.appid and t1.startdate=t2.startdate
) as t2
on t1.personid=t2.personid
Add this to your query:
JOIN (select AppId, MAX(StartDate) as MAxStartDate
from dbo.tbl_Owners
group by PersonId) o2
ON dbo.tbl_Owners.AppId= o2.AppId and
dbo.tbl_Owners.StartDate = o2.MAxStartDate
The sub-query above returns every AppId together with it's latest StartDate. Self-joining with that result will give you what you want.
You can USE CTE for this purpose
;WITH CTE AS
(
SELECT AppID,PersonID,StartDate,
ROW_NUMBER() OVER (PARTITION BY AppID ORDER BY StartDate DESC) RN
FROM TableNAme
GROUP BY AppID,PersonID,StartDate
)
SELECT * FROM CTE
WHERE RN=1
Using row_number
select t.*, p.* -- change as needed
from (select *, rn= row_number() over(partition by AppID order by StartDate desc)
from dbo.tbl_Owners
) t
join dbo.tbl_Persons p on t.rn=1 and t.PersonId = p.PersonId
using cross apply
select t.*, p.* -- change as needed
from dbo.tbl_Persons p
cross apply (
select top(1) *
from dbo.tbl_Owners o
where o.PersonId = p.PersonId
order by o.StartDate desc
) t
SELECT dbo.tbl_Owners.*,dbo.tbl_Persons.PersonFullname FROM dbo.tbl_Owners
INNER JOIN
dbo.tbl_Persons ON dbo.tbl_Owners.PersonID=dbo.tbl_Persons.PersonID
GROUP BY dbo.tbl_Owners.StartDate HAVING MAX(StartDate);
Use GROUP BY on StartDate instead on PersonID.

efficient way to JOIN in SQL

I have to JOIN two tables: articles and sales, but not all the data of articles, just need the last load.
Is there a difference between this two ways? there is a most faster/efficient way? and more important, why?
1)
SELECT *
FROM sales S
INNER JOIN articles A
ON S.article_id = A.article_id AND A.load_date = (SELECT MAX(load_date) FROM articles)
2)
SELECT *
FROM sales s
INNER JOIN (
SELECT *
FROM articles
WHERE load_date = (SELECT MAX(load_date) FROM articles)
) A
ON s.article_id = a.article_id
Most DBMSes also support Windowed Aggregate Functions and a RANK might be more efficient (and easier to write if you're used to it):
SELECT *
FROM
(
SELECT *,
RANK() -- max date gets rank #1
OVER (PARTITION BY a.article_id
ORDER BY a.load_date DESC) rn
FROM sales s
INNER JOIN articles a
ON s.article_id = a.article_id
) dt
WHER rn = 1

Highest Count with a group

I'm having an absolute brain fade
SELECT p.ProductCategory, f.ProductSubCategory, COUNT(*) AS Cnt
FROM Sales f
JOIN Products p ON f.ProductSubCategory = p.ProductSubCategory
GROUP BY p.ProductCategory, f.ProductSubCategory
ORDER BY 1,3 DESC
This shows me the count for each ProductSubCategory, I would like to see only the highest ProductSubCategory per ProductCategory.
I wish to see (I don't care about the Count value)
There are a couple of different ways to do this. One involves joining the results back to themselves and using the max aggregate. But since you are using SQL Server, you can use ROW_NUMBER to achieve the same result:
with cte as (
select p.productcategory, p.ProductSubCategory, COUNT(*) cnt,
ROW_NUMBER() over (partition by p.productcategory order by count(*) desc) rn
from products p
join sales s on p.ProductSubCategory = s.ProductSubCategory
group by p.productcategory, p.ProductSubCategory
)
select *
from cte
where rn = 1
You already got the answer, Please see the following code to. It may help you.
SELECT p.ProductCategory,
f.ProductSubCategory,
COUNT(*) AS Cnt
FROM Sales f
JOIN Products p ON f.ProductSubCategory = p.ProductSubCategory
JOIN (
SELECT p.ProductCategory,
f.ProductSubCategory,
ROW_NUMBER() OVER ( PARTITION BY p.ProductCategory,
f.ProductSubCategory
ORDER BY COUNT(*) DESC) [Row]
FROM Sales f
JOIN Products p ON f.ProductSubCategory = p.ProductSubCategory) Lu
ON P.ProductCategory = Lu.ProductCategory
AND f.ProductSubCategory = Lu.ProductSubCategory
WHERE Lu.Row = 1
GROUP By p.ProductCategory,
f.ProductSubCategory

Getting rows within a specified range for pagination

I am trying to write a query to implement pagination, my basic requirements is that I need a query where I can give min and max range of rows to return for e.g. for page 1 I need record from 1 – 10 for page to 11-20 and so on and so forth.
Through some help form internet and here at SO I have written down the following query but it’s not really working out that way it should and returning me a big sum of rows whatever the range is (probably I am missing some join in the query)
SELECT b.id,b.title,b.name
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY (select NULL as noorder)) AS RowNum, *
FROM [student] b
) as alias,[student] b,[class] c
WHERE b.[status]=1
AND c.id=b.class
AND c.name='Science'
AND RowNum BETWEEN 1 AND 5
ORDER BY b.dtetme DESC
I am lost while fixing in it, can someone please point out the mistake.
Thank you!
Your whole query logic + ROW_NUMBER should go in the sub-query. You use outer WHERE just for paging.
ROW_NUMBER must have ORDER BY on which paging is to be implemented.
SELECT a.id ,
a.title ,
a.name
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY b.dtetme DESC) AS RowNum, b.*
FROM [student] b
INNER JOIN [class] c ON c.id = b.class
WHERE b.[status] = 1
AND c.name = 'Science'
) a
WHERE RowNum BETWEEN 1 AND 10 -- change numbers here for pages
ORDER BY t.RowNum
I think the problem is with th addition of [student] b in the FROM, try moving the join into the subquery.
SELECT a.id, a.title, a.name
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY (select NULL as noorder)) AS RowNum, *
FROM [student] b
JOIN [class] c ON c.id = b.class
WHERE b.[status]=1
AND c.name='Science'
) as a
WHERE a.RowNum BETWEEN 1 AND 5
ORDER BY a.dtetme DESC
Also you may want to consider wrapping this in a procedure or function so you can change the range.
It seems you want something like this:
SELECT t.id,t.title,t.name FROM (
SELECT s.id,s.title,s.name, RowNum = ROW_NUMBER() OVER(ORDER BY s.dtetme DESC)
FROM student s
INNER JOIN class c ON c.id = s.class
WHERE s.[status]=1 AND c.name='Science' ) AS t
WHERE t.RowNum BETWEEN 1 AND 5
ORDER BY t.RowNum