Find most recent records with specific criteria in Access - ms-access

I have a set of records that I would like to display the most recent records that match certain criteria. I've done it wrong in the past where it would first pull the most recent records and THEN go and try and match criteria which would cause some of the records to disappear. What I want to have the query do is to find the records that match criteria first and THEN have it pull the most recent records from that data set. I need to have this query INSERT INTO a Table in Access.
I thought I had it sorted out, but I get an error "Your query does not include the specified expression 'SufGrpID' as part of an aggregate function
An example of the data:
When the query runs, I would like the results to be:
An example of the data:
SufGrpID 03 would be removed from the set because it is not the newest record for CaseID 123
SufGrpID 04 would be removed from the set because it is not of SufTypeID 14 and it is not of Status F
How the data looks
+----------+---------+-------------------------+-----------+--------+
| SufGrpID | CaseID | CreateDate | SufTypeID | Status |
+----------+---------+-------------------------+-----------+--------+
| 01 | 123 | 2010-08-20 07:42:32.000 | 14 | F |
| 02 | 234 | 2010-04-28 10:33:56.000 | 14 | F |
| 03 | 123 | 2010-04-20 10:05:04.000 | 14 | F |
| 04 | 345 | 2010-08-20 11:18:42.000 | 12 | I |
| 05 | 345 | 2010-04-20 11:18:42.000 | 14 | F |
+----------+---------+-------------------------+-----------+--------+
Here's the code that did not work for me...
INSERT INTO [aStudent Base Data] ( [Self Suff ID], [Self Suff Create Date] )
SELECT dbo_sufscrgrp.SufGrpID, Max(dbo_sufscrgrp.CreateDate)
FROM dbo_sufscrgrp
WHERE (((dbo_sufscrgrp.SufTypeID)=14) AND ((dbo_sufscrgrp.Status)="F"))
GROUP BY dbo_sufscrgrp.CaseID;
What I'd like the results to be. (EDITED at 1:33 CST)
+--------------+------------------------+
| Self Suff ID | Self Suff Create Date |
+--------------+------------------------+
| 01 | 2010-08-20 07:42:32.000 |
| 02 | 2010-04-28 10:33:56.000 |
| 05 | 2010-04-20 11:18:42.000 |
+--------------+-------------------------+
Thanks for any help you can give!

Based on the minimal dataset example, consider:
SELECT dbo_sufscrgrp.*
FROM dbo_sufscrgrp
WHERE SufGrpID
IN (SELECT TOP 1 SufGrpID FROM dbo_sufscrgrp As Dupe
WHERE Dupe.CaseID=dbo_sufscrgrp.CaseID AND SufTypeID=14 and Status="F"
ORDER BY Dupe.CreateDate DESC, Dupe.SufGrpID DESC);

Related

How to group and order dependent results by dates in MySQL

I have a database of units which have expiry dates. Some of the units are top level units and have sub-assemblies attached. Sub-assemblies also have expiry dates. I'm trying to create a listing that lists the top-level units ordered by their expiration dates and list all relevant sub-assemblies under them.
The ordering should be done in two different ways:
Query which lists top-level units and their sub-assemblies according to expiry date of the parent item.
Query which lists top-level units and their sub-assemblies ordered by the first expiry date of relevant sub-assembly under them.
Here's example of the master table:
ITEM | NAME | UID | INSTALLED_TO_UID | EXPIRY
AAA | Top_level_unit_1 | 1 | | 2018-03-06
BBB | Sub_assy_1 | 75 | 1 | 2019-06-11
AAA | Top_level_unit_2 | 2 | | 2018-08-12
CCC | Sub_assy_2 | 26 | 1 | 2020-02-05
DDD | Sub_assy_3 | 59 | 2 | 2019-11-11
EEE | Sub_assy_4 | 41 | 2 | 2019-10-30
FFF | Sub_assy_5 | 11 | 1 | 2018-04-10
I am running these now in nested queries (second query inside foreach loop), but i am sure there is better and more efficient way of doing this. At least for case 1.
SELECT *
FROM master
WHERE ITEM = AAA
ORDER
BY EXPIRY ASC
And inside result loop for each result:
SELECT *
FROM master
WHERE INSTALLED_TO_UID = (UID from parent query)
The result should be like:
ITEM | NAME | UID | INSTALLED_TO_UID | EXPIRY
AAA | Top_level_unit_1 | 1 | | 2018-03-06
BBB | Sub_assy_1 | 75 | 1 | 2019-06-11
CCC | Sub_assy_2 | 26 | 1 | 2020-02-05
FFF | Sub_assy_5 | 11 | 1 | 2018-04-10
AAA | Top_level_unit_2 | 2 | | 2018-08-12
DDD | Sub_assy_3 | 59 | 2 | 2019-11-11
EEE | Sub_assy_4 | 41 | 2 | 2019-10-30
For case 2 i don't have a clue yet...
Summary: I would like to have only one query to group and order the results instead of relying to another query in result loop. Secondly i would like to figure out a query to order the results by first expiring child item grouped by parent items
You need to use an outer self join to find the parent record (for query 1) or the first-of-children record (for query 2). Then you can use coalesce to decide which value to take (either from the joined table or the main one) for setting the order.
Query 1:
SELECT record.*
FROM master record
LEFT JOIN master parent
ON record.installed_to_uid = parent.uid
AND parent.installed_to_uid is null
ORDER BY COALESCE(parent.expiry, record.expiry),
COALESCE(parent.uid, record.uid),
COALESCE(record.installed_to_uid,-1),
record.expiry
Query 2:
SELECT record.*
FROM master record
LEFT JOIN (SELECT installed_to_uid, MIN(expiry) expiry
FROM master
WHERE installed_to_uid IS NOT NULL
GROUP BY installed_to_uid) first
ON COALESCE(record.installed_to_uid, record.uid) = first.installed_to_uid
ORDER BY COALESCE(first.expiry, record.expiry),
COALESCE(first.installed_to_uid, record.uid),
COALESCE(record.installed_to_uid,-1),
record.expiry
See it run on sqlfiddle
Although there are surely more efficient solutions, why not start with this:
SELECT *
FROM master
WHERE INSTALLED_TO_UID = (
SELECT UID
FROM master
WHERE ITEM = AAA
)
?

MySQL Count within an IF

+-------------+--------------+----------+-------+
| ticketRefNo | nameOnTicket | boughtBy | event |
+-------------+--------------+----------+-------+
| 38 | J XXXXXXXXX | 2 | 13 |
| 39 | C YYYYYYY | 1 | 13 |
| 40 | M ZZZZZZZZZZ | 3 | 14 |
| 41 | C AAAAAAA | 3 | 15 |
| 42 | D BBBBBB | 3 | 16 |
| 43 | A CCCCC | 3 | 17 |
+-------------+--------------+----------+-------+
+-------------+------------------+--------------+---------------------+--------+
| ticketRefNo | cardNo | cardHolder | exp | issuer |
+-------------+------------------+--------------+---------------------+--------+
| 38 | 4444111133332222 | J McKenny | 2016-01-01 00:00:00 | BOS |
| 39 | 4434111133332222 | C Dempsey | 2016-04-01 00:00:00 | BOS |
| 40 | 4244111133332222 | M Gunn-Davis | 2018-02-01 00:00:00 | RBS |
+-------------+------------------+--------------+---------------------+--------+
+-------------+-------------+----------+
| ticketRefNo | boxOfficeID | paidWith |
+-------------+-------------+----------+
| 41 | 1 | card |
| 42 | 2 | cash |
| 43 | 3 | chequ |
+-------------+-------------+----------+
I have a database with the data shown above. It represents a ticket-buying system. I would like to be able to see a list of tickets bought with the name of the event and either the boxOfficeID or the issuer of the debit card.
I have tried running the following code, to no avail.
SELECT t.ticketRefNo AS 'Reference', t.event AS 'Event',
IF(COUNT(SELECT * FROM Online WHERE t.ticketRefNo=o.ticketRefNo;) >= 1,
o.issuer, InPerson.boxOfficeID) AS 'Card Issuer or Box Office'
FROM Ticket AS t, InPerson, Online AS o
WHERE t.ticketRefNo=o.ticketRefNo;
Cheers in advance!
Some notes: the semicolon character isn't valid syntax; if you have a need to delimit the subquery, wrap it in parens. Escape column aliases like you'd escape any other identifier: use backticks, not single quotes. Single quotes are used around string literals.
Assuming that issuer in the Online table is NOT NULL, and assuming that ticketRefNo is unique in both the Online and InPerson tables, you could do something like this:
SELECT t.ticketRefNo AS `Reference`
, t.event AS `Event`
, IF(o.ticketRefNo IS NOT NULL,o.issuer,i.boxOfficeId)
AS `Card Issuer or Box Office`
FROM Ticket t
LEFT
JOIN InPerson i
ON i.ticketRefNo = t.ticketRefNo
LEFT
JOIN Online o
ON o.ticketRefNo = t.ticketRefNo
Use outer join operations to find matching rows in the InPerson and Online tables, and use a conditional test to see if you got a matching row from the Online table. A NULL will be returned if there wasn't a matching row found.
It's not a good idea to have one column JOINing to two different tables with some values in each of the two tables.
But here goes anyway:
( SELECT ... FROM Ticket t JOIN InPerson x USING(ticketRefNo) ... )
UNION ALL
( SELECT ... FROM Ticket t JOIN Online x USING(ticketRefNo) ... )
ORDER BY ...
The ALL assumes that InPerson and Online never have any overlapping ticketRefNos.
The ORDER BY an the end is in case you want to sort things, although I see no need for it in your attempted SELECT.
The two SELECTs must have the same number of columns.

SQL COUNT query similar to UNION but with results in multiple columns

I have a single-table SQL database built from DHCPD logs, structured as below:
+------+-------+------+----------+---------+-------------------+-----------------+
| id | Month | Day | Time | Type | MAC | ClientIP |
+------+-------+------+----------+---------+-------------------+-----------------+
| 9305 | Nov | 24 | 03:20:00 | DHCPACK | 00:04:f2:4b:dd:51 | 10.123.246.116 |
| 9307 | Nov | 24 | 03:20:07 | DHCPACK | 00:04:f2:99:4c:ba | 10.123.154.176 |
| 9310 | Nov | 24 | 03:20:08 | DHCPACK | 00:19:bb:cf:cd:28 | 10.99.107.3 |
| 9311 | Nov | 24 | 03:20:08 | DHCPACK | 00:19:bb:cf:cd:28 | 10.99.107.3 |
Every DHCP event from the log will eventually make its way into this database, so events from any point in time will be potentially used in the construction of graphs. To make use of the data for graphing, I need to be able to create an output table with multiple columns, but with values derived from a count of those in a single column matching a specific pattern.
The closest thing I've managed to come up with is this query:
select 'Data' as ClientIP, count(*) from Log where ClientIP like '10.99%' and MAC like '00:04:f2%'
union
select 'Voice' as ClientIP, count(*) from Log where ClientIP like '10.123%' and MAC like '00:04:f2%';
Which yields the following result:
+-----------+-------+
| ClientIP | Count |
+-----------+-------+
| Data | 4618 |
| Voice | 13876 |
+-----------+-------+
Fine for a one-off query, but I want to take those two rows, turn them into two columns, and run the same query with one row per hour (for instance). I want something like this:
+------+-------+------+
| Hour | Voice | Data |
+------+-------+------+
| 03 | 22 | 4 |
| 04 | 123 | 23 |
| 05 | 45 | 5 |
Any advice is greatly welcomed.
Thanks
You can group by hour and use conditional computation to count Data and Voice traffic.
For example:
SELECT
HOUR(time) AS `Hour`,
SUM(CASE WHEN ClientIP like '10.99%' and MAC like '00:04:f2%' THEN 1 ELSE 0 END) AS `Data`,
SUM(CASE WHEN ClientIP like '10.123%' and MAC like '00:04:f2%' THEN 1 ELSE 0 END) AS `Voice`
FROM log
GROUP BY HOUR(time)
Create a separate table for (as you want) :
+------+-------+------+
| Hour | Voice | Data |
+------+-------+------+
and update it every hour using triggers.

How to update one table in MySQL after insertion in another table

I have two table first is "employee_details" second is "attendance_rule"
employee_details
e_code | e_name | e_type
01 | Ram | Regular
02 | Shyam | Contract
03 | Mohan | Regular
attendance_rule
e_code | e_type | casual_leaves
01 | Regular | 7
02 | Contract | 6
03 | Regular | 7
I have manually insert values in "attendance_rule".
Now the problem is that when the attendance rule changes in the organisation. The user has to manually update the casual_leave coloumn for all the employees.
What I want is that when an employee details are added in employee_details table , the entry in the attendance_rule table should be automatically made having e_code and e_type same as e_code and e_type of employee_details table and casual_leaves equals to the casual_leaves of the same type of employee.
I think that a trigger should be used here.
but I dont know how to use trigger for this condition i.e. for the entry of casual leaves.
Please help me...how should I do that? Is there a way to do it other that trigger?
It sounds like you don't need a separate entry in attendance_rule for every employee, just for every employee type, so you don't need a trigger at all.
Create employee_details like you suggested:
e_code | e_name | e_type
01 | Ram | Regular
02 | Shyam | Contract
03 | Mohan | Regular
But create attendance_rule with only the rules for each type:
e_type | casual_leaves
Regular | 7
Contract | 6
If you need to find the number of casual leaves for a particular employee, join the two tables:
select
e.e_code,
e.e_name,
e.e_type,
a.casual_leaves
from employee_details e
inner join attendance_rule a on e.e_type = a.e_type
to get
e_code | e_name | e_type | casual_leaves
01 | Ram | Regular | 7
02 | Shyam | Contract | 6
03 | Mohan | Regular | 7
And if you want to make it as easy to use as a table, you can create a view:
create view emp_all_details as
select
e.e_code,
e.e_name,
e.e_type,
a.casual_leaves
from employee_details e
inner join attendance_rule a on e.e_type = a.e_type
Now you can simply say: select * from emp_all_details to get:
e_code | e_name | e_type | casual_leaves
01 | Ram | Regular | 7
02 | Shyam | Contract | 6
03 | Mohan | Regular | 7

MySQL using GROUP BY to group by multiple columns

I'd like to use GROUP BY multiple columns, I think it's best to start with an example:
SELECT
eventsviews.eventId,
showsActive.showId,
showsActive.venueId,
COUNT(*) AS count
FROM eventsviews
INNER JOIN events ON events.eventId = eventsviews.eventId
INNER JOIN showsActive ON showsActive.eventId = eventsviews.eventId
WHERE events.status = 1
GROUP BY showsActive.venueId, showsActive.showId, showsActive.eventId
ORDER BY count DESC
LIMIT 100;
Output:
| *eventId* | *showId* | *venueId* | *count* |
+-----------+----------+-----------+---------+
[...snip...]
| 95 | 92099 | 9770 | 32 |
| 95 | 105472 | 10702 | 32 |
| 3804 | 41225 | 8165 | 17 |
| 3804 | 41226 | 8165 | 17 |
| 923 | 2866 | 5451 | 14 |
| 923 | 20184 | 5930 | 14 |
[...snip...]
What I would like instead:
| *eventId* | *showId* | *venueId* | *count* |
+-----------+----------+-----------+---------+
| 95 | 92099 | 9770 | 32 |
| 3804 | 41226 | 8165 | 17 |
| 923 | 20184 | 5930 | 14 |
So, I want my data grouped by eventId, but only once for each showId and venueId ...
I actually have a SQL query that does that, but it has 8 subqueries and is as slow as a T-Ford ... And since this is executed on every page load, speeding things up looks like a good idea!
There are a few questions like this, and I've tried many different things, but I've been at this query for an hour and I can't seem to get it to work as I want :-(
Thanks!
You probably want either a min or a max on showid, and then not include it in the group by, I can't tell which because looking at your "prefered" resultset, you have both.
If you want your data grouped by eventId, group just by eventId and you'll get exactly the result you're looking for.
This is a MySQL feature (?) that it allows you to select non-aggregate columns, in which case it will return the first row available. In other DBMS it's achieved by DISTINCT ON, which is not available in MySQL.