Selecting nth row in Inner Join on Microsoft SQL Server 2012 - sql

My gifts table has multiple rows that match the criteria in the inner join statement below. I want to select a specific row (3rd, 5th or 32nd row as examples). I have tried using Row_Number(), but it is a windowed function and needs to be within the select part (as far as I know), but that then throws errors, because the overall statement has to return only one value.
I have also tried using top and then selecting the bottom value of top (top 5 for example and then selecting the last value), but I was unable to get that to work. With the statement below, how can I modify it to also select the nth row?
update output
set output.gift_date01 = (select gifts.gift_date
where
gifts.gift_date >= '2015-1-1 00:00:00'
and gifts.gift_date <= '2015-12-31')
from output
inner join gifts on output.donor_id = gifts.donor_id;
Thank you!

I think that this will get you close if it's not exactly what you're looking to do.
;WITH CTE_Gifts AS
(
SELECT
G.donor_id,
G.gift_date,
ROW_NUMBER() OVER (PARTITION BY donor_id ORDER BY gift_date) AS row_num
FROM
dbo.Gifts G
WHERE
G.gift_date BETWEEN #start_date AND #end_date
)
UPDATE O
FROM
[Output] O -- Should be renamed to not a reserved keyword
INNER JOIN CTE_Gifts C ON
C.donor_id = O.donor_id AND
C.row_num = #some_row_number

Related

Limit number of rows fetching in a left outer join in Oracle

I'm going to create a data model in oracle fusion applications. I need to create column End_date based on two tables in the query. So I used two methods.
Using a subquery:
SELECT *
FROM (SELECT projects_A.end_date
FROM projects_A, projects_B
WHERE projects_A.p_id = projects_B.p_id
AND rownum = 1)
Using a LEFT OUTER JOIN:
SELECT projects_A.end_date
FROM projects_A
LEFT JOIN projects_B
ON projects_A.p_id = projects_B.p_id
WHERE rownum = 1
Here when I used a subquery, the query returns the results as expected. But when I use left outer join with WHERE rownum = 1 the result is zero. Without WHERE rownum = 1 it retrieves all the results. But I want only the first result. So how can I do that using left outer join? Thank you.
Looks like you want to bring a non-null end_date value(So, add NULLS LAST), but the sorting order is not determined yet(you might add a DESC to the end of the ORDER BY clause depending on this fact ), and use FETCH clause(the DB version is 12c+ as understood from the comment) with ONLY option to exclude ties as yo want to bring only single row.
So, you can use the following query :
SELECT A.end_date
FROM projects_A A
LEFT JOIN projects_B B
ON A.p_id = B.p_id
ORDER BY end_date NULLS LAST
FETCH FIRST 1 ROW ONLY

SQL Server 2016 Sub Query Guidance

I am currently working on an assignment for my SQL class and I am stuck. I'm not looking for full code to answer the question, just a little nudge in the right direction. If you do provide full code would you mind a small explanation as to why you did it that way (so I can actually learn something.)
Here is the question:
Write a SELECT statement that returns three columns: EmailAddress, ShipmentId, and the order total for each Client. To do this, you can group the result set by the EmailAddress and ShipmentId columns. In addition, you must calculate the order total from the columns in the ShipItems table.
Write a second SELECT statement that uses the first SELECT statement in its FROM clause. The main query should return two columns: the Client’s email address and the largest order for that Client. To do this, you can group the result set by the EmailAddress column.
I am confused on how to pull in the EmailAddress column from the Clients table, as in order to join it I have to bring in other tables that aren't being used. I am assuming there is an easier way to do this using sub Queries as that is what we are working on at the time.
Think of SQL as working with sets of data as opposed to just tables. Tables are merely a set of data. So when you view data this way you immediately see that the query below returns a set of data consisting of the entirety of another set, being a table:
SELECT * FROM MyTable1
Now, if you were to only get the first two columns from MyTable1 you would return a different set that consisted only of columns 1 and 2:
SELECT col1, col2 FROM MyTable1
Now you can treat this second set, a subset of data as a "table" as well and query it like this:
SELECT
*
FROM (
SELECT
col1,
col2
FROM
MyTable1
)
This will return all the columns from the two columns provided in the inner set.
So, your inner query, which I won't write for you since you appear to be a student, and that wouldn't be right for me to give you the entire answer, would be a query consisting of a GROUP BY clause and a SUM of the order value field. But the key thing you need to understand is this set thinking: you can just wrap the ENTIRE query inside brackets and treat it as a table the way I have done above. Hopefully this helps.
You need a subquery, like this:
select emailaddress, max(OrderTotal) as MaxOrder
from
( -- Open the subquery
select Cl.emailaddress,
Sh.ShipmentID,
sum(SI.Value) as OrderTotal -- Use the line item value column in here
from Client Cl -- First table
inner join Shipments Sh -- Join the shipments
on Sh.ClientID = Cl.ClientID
inner join ShipItem SI -- Now the items
on SI.ShipmentID = Sh.ShipmentID
group by C1.emailaddress, Sh.ShipmentID -- here's your grouping for the sum() aggregation
) -- Close subquery
group by emailaddress -- group for the max()
For the first query you can join the Clients to Shipments (on ClientId).
And Shipments to the ShipItems table (on ShipmentId).
Then group the results, and count or sum the total you need.
Using aliases for the tables is usefull, certainly when you select fields from the joined tables that have the same column name.
select
c.EmailAddress,
i.ShipmentId,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
order by i.ShipmentId, c.EmailAddress;
Using that grouped query in a subquery, you can get the Maximum total per EmailAddress.
select EmailAddress,
-- max(TotalShipItems) as MaxTotalShipItems,
max(TotalPriceDiscounted) as MaxTotalPriceDiscounted
from (
select
c.EmailAddress,
-- i.ShipmentId,
-- count(*) as TotalShipItems,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
) q
group by EmailAddress
order by EmailAddress
Note that an ORDER BY is mostly meaningless inside a subquery if you don't use TOP.

How to display last updated record for multiple records

I have joined two tables to pull the data I need. I'm having trouble only displaying the most current record from one table. What I'm trying to do is look for the last updated value. I have tried to incorporate max() and row_num but have not had any success.
Here is what I currently have:
select distinct t1.CaId,t1.Enrolled,t1.Plan,t2.Category,t2.updateddate
from table.one(nolock) t1
inner join table.two(nolock) t2 on t1.CaId=t2.CaID
where t1.coverageyear=2016
and right(t1.Plan,2)<>left(t2.Category,2)
order by 5 desc
You can join your main query with a subquery that just grabs the last update date for each ID, like this:
select all_rec.CaId, all_rec.Enrolled, all_rec.[Plan], all_rec.Category, all_rec.updateddate
from
(select distinct t1.CaId,t1.Enrolled,t1.[Plan],t2.Category,t2.updateddate
from [table.one](nolock) t1
inner join [table.two](nolock) t2 on t1.CaId=t2.CaID
where t1.coverageyear=2016
and right(t1.[Plan],2)<>left(t2.Category,2)
) as all_rec
inner join
(SELECT max(updateddate) AS LAST_DATE, CaId
FROM [table.two](nolock)
GROUP BY CaId)
AS GRAB_DATE
on (all_rec.Ca_Id = GRAB_DATE.Ca_Id)
and (all_rec.updateddate = GRAB_DATE.updateddate)
order by 5 desc
I added brackets around your usages of table and Plan because those are SQL reserved words.
If you are trying to get last updated value, then just simply add to your query:
order by t2.updateddate desc
It will show most current record from tables.

Access Inner Join to select a record with the Max Date to distinctively select a record from another table

I am using Access and writing a query to join another table which has multiple records with the same ID. So, I only need to fetch the record with the MAX date to fetch only 1 record. I keep getting an syntax error.
Here is my normal Inner Join to the other table:
SELECT
table2.NameDesc,
FROM [table1]
INNER JOIN
(
SELECT [MyDataTable].[ID], [MyDataTable].[NameDesc], MAX( [MyDataTable].[MyDate]) as RecentDate
FROM [MyDataTable]
GROUP BY [MyDataTable].[ID]
) table2
ON [table1].[ID] = table2.[ID];
Normally on my inner join statement, I write like this:
INNER JOIN [table3] ON [table1].[ID] = [table3].[ID])
So I need to fetch only 1 record with the MAX date. I finally was able to
click save but when I click on the DateSheetView, I get the following error:
"Your query does not include the specified expression "NameDesc" as part of an aggregate function"
I am new the access query with the [ ] bracket which is different from sql server.
How can I get this Access query to work and fetch the record with the max date from another table?
Any help is appreaciated.
Thanks
When you use an aggregate function, like MAX, you need to include all other columns you are returning as part of the group by. In order for the query to run without errors, you can do this:
SELECT
table2.NameDesc
FROM [table1]
INNER JOIN
(
SELECT [MyDataTable].[ID],
[MyDataTable].[NameDesc],
MAX([MyDataTable].[MyDate]) as RecentDate
FROM [MyDataTable]
GROUP BY [MyDataTable].[ID],
[MyDataTable].[NameDesc]
) table2
ON [table1].[ID] = table2.[ID];

update a sql using subquery and aggregate function

Below is a SQL query where I am getting the error
returns more than 1 value
Query:
update Tab2
set monthly_calls =
(select a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid)
FYI... my select query which is left of = is working fine
If you select query which is left of = returns multiple records then it will not be executed.
So please try to get only 1 record out of that.
Only one record can be updates at a time using this update statement, so you must make sure that only one record is returned by that query.
Try this.
update Tab2
set monthly_calls =
(select top 1 a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid)
update Tab2
set monthly_calls = a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid
I think the logic you're using needs to be reviewed as well. The column annual_calls - does it get updated once a year, or is it updated at the end of every month? The reason I ask is if this were for a new system with only 6 months worth of calls, the monthly_calls field would be set to a value of approximately half of what it should be.
I would suggest also renaming monthly_calls to avg_monthly_calls or something more meaningful because if someone were to come in, look at your database schema and try to work out what is what, they might think that field holds total monthly calls or expected monthly calls.
As for the statement to update the data - without seeing any sort of data, here's a neater way of using an UPDATE statement without subselects:
UPDATE
Tab2
SET
monthly_calls = MAX(annual_calls) / 12
FROM
CPO INNER JOIN Tab2 ON CPO.accountid = Tab2.accountid
WHERE
YEAR([Tab2.DateField]) = #YearToUpdate
GROUP BY
CPO.accountid
This basically takes care of the subselect by including a FROM clause in the UPDATE statement. That way you can join and update all at once.
Edit: I would also add in a WHERE clause so you can set it so you update only the current year, or the previous year, or whatever year/period you pass in. That way, you ensure you only get one record for each account for each year.
You are trying to set monthly_calls variable's value by using a select statement that returns a set of rows. Try using an aggregate function on a.month like max().
Standard SQL requires scalar subqueries, which in this case is arguably simpler than joins anyhow:
UPDATE Tab2
SET monthly_calls = (
SELECT MAX(annual_calls) / 12
FROM cpo
WHERE tab2.accountid = cpo.accountid
)
WHERE EXISTS (
SELECT *
FROM cpo
WHERE tab2.accountid = cpo.accountid
);