SQL select record for each id where max year equals value - sql

There is a lot of info on this but nothing seems to match my issue exactly and I am stuck.
I have two tables, 'members' and 'payment_records'. The 'members' table includes a unique member_id as well as other data (address, payment_type etc). The 'payment_records' table includes a unique payment_id, plus the member_id relative to who made the payment, and an expiry_year (eg 2011, 2012, 2013 etc). Each member_id has multiple payment records.
I need to get the records where the MAXIMUM expiry_year for each member_id matches a dynamic value ($year). Right now I am returning records where all members have an expiry_year = $year, even if it's not the MAXIMUM value. For example, Mary might have 2 payments with expiry years of 2013 and 2014, so she shows in my results for $year = 2013, even though her most recent result is 2014. How do I limit it so records are only returned if the maximum expiry_year is = $year?
This is my (simplified) statement now, that returns records with the correct expiry, but doesn't weed out the ones with a newer year:
SELECT members.member_id, payment_records.expiry_year, payment_records.member_id
FROM members inner join payment_records on members.member_id=payment_records.member_id
WHERE member_category='Standard' AND payment_records.expiry_year = $year"
I have tried using a sub statement but am confused as to the correct syntax in amongst my inner join statement. I've also tried using DISTINCT and GROUP BY with ORDER BY but it just gives me blank results.
Any help is appreciated in advance. I am sorry if this is a double-up, I've been searching tutorials till my eyeballs ached with no luck.

You want to use group by and having for this logic:
SELECT members.member_id, MAX(pr.expiry_year)
FROM members m inner join
payment_records pr
on m.member_id = pr.member_id
WHERE m.member_category = 'Standard'
GROUP BY m.member_id
HAVING MAX(pr.expiry_year) = $year;

Related

Counting Unique IDs In a LEFT/RIGHT JOIN Query in Access

I am working on a database to track staff productivity. Two of the ways we do that is by monitoring the number of orders they fulfil and by tracking their error rate.
Each order they finish is recorded in a table. In one day they can complete many orders.
It is also possible for a single order to have multiple errors.
I am trying to create a query that provides a summary of their results. This query should have one column with "TotalOrders" and another with "TotalErrors".
I connect the two tables with a LEFT/RIGHT join since not all orders will have errors.
The problem comes when I want to total the number of orders. If someone made multiple mistakes on an order, that order gets counted multiple times; once for each error.
I want to modify my query so that when counting the number of orders it only counts records with distinct OrderID's; yet, in the same query, also count the total errors without losing any.
Is this possible?
Here is my SQL
SELECT Count(tblTickets.TicketID) AS TotalOrders,
Count(tblErrors.ErrorID) AS TotalErrors
FROM tblTickets
LEFT JOIN tblErrors ON tblTickets.TicketID = tblErrors.TicketID;
I have played around with SELECT DISTINCT and UNION but am struggling with the correct syntax in Access. Also, a lot of the examples I have seen are trying to total a single field rather than two fields in different ways.
To be clear when totalling the OrderCount field I want to only count records with DISTINCT TicketID's. When totalling the ErrorCount field I want to count ALL errors.
Ticket = Order.
Query Result: Order Count Too High
Ticket/Order Table: Total of 14 records
Error Table: You can see two errors for the same order on 8th
do a query that counts orders by staff from Orders table and a query that counts errors by staff from Errors table then join those two queries to Staff table (queries can be nested for one long SQL statement)
correlated subqueries
SELECT Staff.*,
(SELECT Count(*) FROM Orders WHERE StaffID = Staff.ID) AS CntOrders,
(SELECT Count(*) FROM Errors WHERE StaffID = Staff.ID) AS CntErrors
FROM Staff;
use DCount() domain aggregate function
Option 1 is probably the most efficient and option 3 the least.

Query build to find records where all of a series of records have a value

Let me explain a little bit about what I am trying to do because I dont even know the vocab to use to ask. I have an Access 2016 database that records staff QA data. When a staff member misses a QA we assign a job aid that explains the process and they can optionally send back a worksheet showing they learned about what was missed. If they do all of these ina 3 month period they get a credit on their QA score. So I have a series of records all of whom have a date we assigned the work(RA1) and MAY have a work returned date(RC1).
In the below image "lavalleer" has earned the credit because both of her sheets got returned. "maduncn" Did not earn the credit because he didn't do one.
I want to create a query that returns to me only the people that are like "lavalleer". I tried hitting google and searched here and access.programmers.co.uk but I'm only coming up with instructions to use Not null statements. That wouldn't work for me because if I did a IS Not Null on "maduncn" I would get the 4 records but it would exclude the null.
What I need to do is build a query where I can see staff that have dates in ALL of their RC1 fields. If any of their RC1 fields are blank I dont want them to return.
Consider:
SELECT * FROM tablename WHERE NOT UserLogin IN (SELECT UserLogin FROM tablename WHERE RCI IS NULL);
You could use a not exists clause with a correlated subquery, e.g.
select t.* from YourTable t where not exists
(select 1 from YourTable u where t.userlogin = u.userlogin and u.rc1 is null)
Here, select 1 is used purely for optimisation - we don't care what the query returns, just that it has records (or doesn't have records).
Or, you could use a left join to exclude those users for which there is a null rc1 record, e.g.:
select t.* from YourTable t left join
(select u.userlogin from YourTable u where u.rc1 is null) v on t.userlogin = v.userlogin
where v.userlogin is null
In all of the above, change all occurrences of YourTable to the name of your table.

Record with latest date, where date comes from a joined table

I have tried every answer that I have found to finding the last record, and I have failed in getting a successful result. I currently have a query that lists active trailers. I am needing it to only show a single row for each trailer entry, where that row is based on a date in a joined table.
I have tables
trailer, company, equipment_group, movement, stop
In order to connect trailer to stop (which is where the date is), i have to join it to equipment group, which joins to movement, which then joins to stop.
I have tried using MAX and GROUP BY, and PARTITION BY, both of which error out.
I have tried many solutions here, as well as these
https://thoughtbot.com/blog/ordering-within-a-sql-group-by-clause
https://www.geeksengine.com/article/get-single-record-from-duplicates.html
It seems that all of these solutions have the date in the same table as the thing that they want to group by, which I do not.
SELECT
trailer.*
company.name,
equipment_group.currentmovement_id,
equipment_group.company_id,
movement.dest_stop_id, stop.location_id,
stop.*
FROM trailer
LEFT OUTER JOIN company ON (company.id = trailer.company_id)
LEFT OUTER JOIN equipment_group ON (equipment_group.id =
trailer.currenteqpgrpid)
LEFT OUTER JOIN movement ON (movement.id =
equipment_group.currentmovement_id)
LEFT OUTER JOIN stop ON (stop.id = movement.dest_stop_id)
WHERE trailer.is_active = 'A'
Using MAX and GROUP BY gives error "invalid in the select list... not contained in...aggregate function"
Welllllll, I never did end up figuring that out, but if I joined movements on to equipment group by two conditions, all is well. Each extra record was created by each company id.... company id is in EVERY table.

SQL: If there is No match on condition (row in table) return a default value and Rows which match from different tables

I have three tables: Clinic, Stock and StockLog.
I need to get all rows where Stock.stock < 5. I need to also show if an order has been placed and what amount it is; which is found in the table Stocklog.
The issue is that a user can set his stock level in the Stock table without placing an order which would go to Stocklog.
I need a query that can : return the rows in the Stock table and get the related order amounts in the Stocklog table. If no order has been placed in StockLog, then set amount to order amount to zero.
I have tried :
SELECT
Clinic.Name,
Stock.NameOfMedication, Stock.Stock,
StockLog.OrderAmount
FROM
Clinic
JOIN
Stock ON Stock.ClinicID = Clinic.ClinicID
JOIN
StockLog ON StockLog.StockID = Stock.StockID
WHERE
Stock.Stock <= 5
The issue with my query is that I lose rows which are not found in StockLog.
Any help on how to write this.
Thank you.
I am thinking the query should look like this:
SELECT c.Name, s.NameOfMedication, s.Stock,
COALESCE(sl.OrderAmount, 0) as OrderAmount
FROM Stock s LEFT JOIN
Clinic c
ON s.ClinicID = c.ClinicID LEFT JOIN
StockLog sl
ON sl.StockID = s.StockID
WHERE s.Stock <= 5 ;
You want to keep all rows in Stock (subject to the WHERE condition). So think: "make Stock the first table in the FROM and use LEFT JOIN for all the other tables."
If you want to keep all the rows that result from joining Clinic and Stock, then use a LEFT OUTER JOIN with StockLog. I don't know which SQL you're using (SQL Server, MySQL, PostgreSQL, Oracle), so I can't give you a precise example, but searching for "left outer join" in the relevant documentation should work.
See this Stack Overflow post for an explanation of the various kinds of joins.

ssrs hidden rows are getting summed

I am fairly new to SSRS and SQL Server and I need help with the following issue:
I have two tables. One contains the time stamped record of purchases of one single product by a small group of customers. The other table contains the time stamped record of price changes for that product.
Table1
PurchaseDateTime - primary key,
CustCode, kgs
Table2
Price, ChangeDateTime
I can display all required information from the two tables in a tablix using the following query:
SELECT input.purchasedatetime,
input.custcode,
input.kgs,
price.price,
price.changedatetime
FROM input
LEFT OUTER JOIN price
ON input.purchasedatetime > price.changedatetime
However, the query correctly returns the results which includes the superseded prices as well. I could hide those rows, but the totals still sum it all. I could group it by the Price.ChangeDateTime as the third subgroup and then filter it on TopN, but the result is the same. The display is correct, the totals are not, which is probably to be expected since the totals are on the higher level grouping.
I cannot apply the conditions for the visibility to the SUM() as it does not allow aggregates and I am hiding on Previous().
I would prefer the query to do the complete work and I have seen that some other databases implement the LIMIT 1 clause, but here this is not supported.
I am still to try the custom code on the report level, but I am not too familiar with VB code.
I am using the SQL Server 2014 Express and Visual Studio 2013 integrated shell as Data Tools or BI
Any help is much appreciated.
you can get maximum change date time for each purchase time using GROUP BY and MAX
These values can be joined further to get price for each purchase time
SQL Fiddle
with cte
as
(
SELECT input.purchasedatetime, MAX(price.changedatetime) as ChangeDateTime
FROM input
LEFT OUTER JOIN price
ON input.purchasedatetime > price.changedatetime
GROUP BY input.purchasedatetime
)
SELECT input.purchasedatetime,
input.custcode,
input.kgs,
price.price,
price.changedatetime
FROM input
JOIN cte
on input.purchasedatetime = cte.purchasedatetime
left join price
on cte.changedatetime = price.changedatetime