Exposing more fields on group by sql - sql

I know, in a Group By you can't Select a field that is not in an aggregate function or the GROUP BY clause.
However, There must be a workaround using joins or something else.
I have TWO tables BMP_VISITS_SITES and BMP_VISITS_COMMENTS which are connected by StationID in a one-to-many relationship. One Site can have many comments.
I'm trying to write a query that returns all Sites and the latest (only 1) comment. I have a "working" query but it only returns two columns which are in either an aggregate function or group by.
Here is my "working" query:
select a.StationID,
MAX(b.[dateobserved]) as LastDateObserved,
a.Status
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID;
But how can I access all the columns in both tables?
I've tried inner joins with 1/2 success. When I join my BMP_VISITS_SITES to the above query I get all the fields of the table (t1). Great, but as soon as I try joining on BMP_VISITS_COMMENTS (t3) I get more results than I should.
select t1.*, t2.*
--,t3.*
from BMP_VISITS_SITES t1
inner join (
select a.StationID, MAX(b.[dateobserved]) as LastDateObserved from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID
) t2 on t2.StationID = t1.StationID
--inner join sde.BMP_VISITS_COMMENTS t3 on t3.StationID = t2.StationID;

SELECT a.*, b.* FROM
BMP_VISITS_SITES a
OUTER APPLY
(
SELECT TOP 1 *
FROM BMP_VISITS_COMMENTS b
WHERE b.StationID = a.StationID
ORDER BY LastDateObserved DESC
) b
You can use apply to get the last comment record and return all fields from both sides of the query.

Use row_number()
select *
from
(
select a.StationID,
a.Status,
b.*,
row_number() over (partition by a.stationid, a.status order by b.[dateobserved] desc) as rn
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
) v
where rn = 1

Related

Cross join on a group based on the condition in SQL Server

I have Item and Exam tables -
My desire result -
I tried a lot but I am unable to join Exam table to Item table based on Code group.
Is it possible to cross join or any other join for these two tables to get the desire result?
You can try this
SELECT A.Code, i.Item, A.Exam
FROM
(
SELECT Code,e.Exam
FROM Item i
CROSS JOIN Exam e
GROUP BY Code,e.Exam
) A
LEFT JOIN Item i ON i.Code= A.Code AND i.Exam = A.Exam
I don't see a way around generating the missing data. The calendar table approach would be to cross join all codes with all exams. Then, left join this table to Item and order to get the result you want:
WITH cte AS (
SELECT *
FROM (SELECT DISTINCT Code FROM Item) AS C
CROSS JOIN Exam
)
SELECT
t1.Code,
t2.Item,
t1.Exam
FROM cte t1
LEFT JOIN Item t2
ON t1.Code = t2.Code AND
t1.Exam = t2.Exam
ORDER BY
t1.Code,
CASE WHEN t2.Item IS NOT NULL THEN 0 ELSE 1 END,
t1.Exam
Demo here:
Rextester
This should produce what you need. I am using UNION to combine CROSS JOIN and item table and then group by to ensure NULLs are removed.
SELECT code, max(item) as item, exam
FROM
(SELECT distinct i.code, null as item, e.exam FROM exam e cross join item i
union all
SELECT code, item, exam
FROM item) u
group by code, exam
order by code, exam

selecting records from main table and count of each row in another table

I have 2 table in my database that tables are in relationship with foreign key
I want to select all records from main table and then select count of each row in another table than have same ID from main table I tried to create a select query but it is not work correctly
this query return all records from main table + count of all records from next table(not count of each row in relationship)
SELECT tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID,
tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc,
(SELECT COUNT(dbo.tblForumPosts.id) AS Expr1
FROM dbo.tblForumSubGroups INNER JOIN dbo.tblForumPosts ON
dbo.tblForumSubGroups.id = dbo.tblForumPosts.SubGroupID) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1 INNER JOIN
dbo.tblForumPosts AS tblForumPosts_1 ON tblForumSubGroups_1.id
= tblForumPosts_1.SubGroupID
SELECT tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID, tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc,
COUNT(tblForumPosts_1.id) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1
INNER JOIN dbo.tblForumPosts AS tblForumPosts_1 ON tblForumSubGroups_1.id = tblForumPosts_1.SubGroupID
GROUP BY tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID, tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc
I would suggest cross apply as you can do a lot more things with it ...
SELECT t1.id,
t1.GroupID,
t1.SubGroupTitle,
t1.SubGroupDesc,
t2.val
FROM dbo.tblForumSubGroups AS t1
cross apply (SELECT COUNT(*)
FROM dbo.tblForumPosts as t2
WHERE t1.id = t2.SubGroupID) x(val)
Do not mix sub-query and join logic. Use only one of them. I prefer sub-select.
SELECT tblForumSubGroups_1.id,
tblForumSubGroups_1.GroupID,
tblForumSubGroups_1.SubGroupTitle,
tblForumSubGroups_1.SubGroupDesc,
(SELECT COUNT(*)
FROM dbo.tblForumPosts
WHERE dbo.tblForumSubGroups.id = dbo.tblForumPosts.SubGroupID) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1
Just to supply another answer though I believe the cross apply is likely the best option:
SELECT
A.id, A.GroupID, A.SubGroupTitle, A.SubGroupDesc,
B.IDCount AS Expr1
FROM dbo.tblForumSubGroups A
INNER JOIN (
Select SubGroupID, Count(ID) as IDCount
from dbo.tblForumPosts
Group By SubGroupID
) B On A.ID = B.SubGroupID

SQL Subquery to get first record

I need to execute a query something like below.
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
(SELECT * FROM (
SELECT ROUND(LAST_ORDER_AMOUNT,5) FROM ORDERS WHERE ID=A.id AND REQUEST_LEVEL='N' ORDER BY O_DATE DESC)
WHERE ROWNUM =1) AS AMOUNT
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
But this gives me A.ID is invalid error in oracle. I need to get the first record from inner query as it will return multiple records.
Can someone please let me know how can i bind these tables to achieve my goal.
Thank you in advance.
You can rewrite subquery using WITH clause, not exactly sure on syntax but should be something like following.
WITH AmountQuery
AS (
SELECT ID
,ROUND(LAST_ORDER_AMOUNT, 5) AS AmountValue
,ROW_NUMBER() OVER ( ORDER BY O_DATE DESC ) AS RN
FROM ORDERS
WHERE REQUEST_LEVEL = 'N'
)
SELECT TO_CHAR(ROWNUM)
,A.Name
,B.Order
,C.AmountValue
FROM Table1 A
LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN AmountQuery C
ON a.ID = c.ID
AND c.RN = 1
here is SQLFiddle to show how it works.
http://sqlfiddle.com/#!4/696b6/36
Probably, LIMIT will do the job for you selecting just one record from the subquery (It worked for me in MySQL. I do not have Oracle, but I think it may be similar). Try something like this:
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
COALESCE( C.AMOUNT ) as AMOUNT,
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN ( SELECT ROUND(LAST_ORDER_AMOUNT,5) AS AMOUNT FROM ORDERS WHERE REQUEST_LEVEL='N' ORDER BY O_DATE DESC ) C ON C.ID = A.id
group by A.id;

Multi-part identifier could not be bound on inner join with sub query

I'm trying to join a main table with just the first record of a related table so I only get one result back
SELECT
*
FROM
admissions as a
inner join (select
top 1 *
from
admissionroom
where
admissionroom.admissionid = a.id
order by
admissionroom.startdate asc) as r on
a.id = r.admissionid
I've stripped out all other parts of the query as this is the piece causing the error.
You can't do that on a JOIN, you need to use CROSS APPLY:
SELECT *
FROM admissions as a
CROSS APPLY (SELECT TOP 1 *
FROM admissionroom
WHERE admissionid = a.id
ORDER BY startdate ASC) as r

SQL: Turn a subquery into a join: How to refer to outside table in nested join where clause?

I am trying to change my sub-query in to a join where it selects only one record in the sub-query. It seems to run the sub-query for each found record, taking over a minute to execute:
select afield1, afield2, (
select top 1 b.field1
from anothertable as b
where b.aForeignKey = a.id
order by field1
) as bfield1
from sometable as a
If I try to only select related records, it doesn't know how to bind a.id in the nested select.
select afield1, afield2, bfield1
from sometable a left join (
select top 1 id, bfield, aForeignKey
from anothertable
where anothertable.aForeignKey = a.id
order by bfield) b on
b.aForeignKey = a.id
-- Results in the multi-part identifier "a.id" could not be bound
If I hard code values in the nested where clause, the select duration drops from 60 seconds to under five. Anyone have any suggestions on how to join the two tables while not processing every record in the inner table?
EDIT:
I ended up adding
left outer join (
select *, row_number() over (partition by / order by) as rank) b on
b.aforeignkey = a.id and b.rank = 1
went from ~50 seconds to 8 for 22M rows.
Try this:
WITH qry AS
(
SELECT afield1,
afield2,
b.field1 AS bfield1,
ROW_NUMBER() OVER(PARTITION BY a.id ORDER BY field1) rn
FROM sometable a LEFT JOIN anothertable b
ON b.aForeignKey = a.id
)
SELECT *
FROM qry
WHERE rn = 1
Try this
select afield1,
afield2,
bfield1
from sometable a
left join
(select top 1 id, bfield, aForeignKey from anothertable where aForeignKey in(a.id) order by bfield) b on b.aForeignKey = a.id