Using MAX function in a subquery in Access - sql

In Microsoft Access, I'm querying data from four tables and I'm using the MAX function to display the most recent records. The code below works but it's using two queries linked together. By using a sub-query, shown below with the full code, the code takes an hour to run, which is why I using two queries. Is there a better way of doing this?
QUERY 1
SELECT
a.office_id AS ofid,
rc.recorder_id AS recorder,
a.cust_name,
r.account_id,
rc.lpc_phone,
rc.device_serial,
rc.device_mfg,
rc.device_type
INTO RDS_INFO
FROM MaxDates
INNER JOIN (status AS s INNER JOIN ((config_recorder AS rc INNER JOIN recorders AS r ON rc.recorder_id = r.recorder_id)
INNER JOIN accounts AS a ON r.account_id = a.account_id) ON s.status = rc.row_status) ON (MaxDates.MaxOftrans_datetime = rc.trans_datetime) AND (MaxDates.recorder_id = rc.recorder_id)
WHERE (((rc.lpc_phone) Is Not Null
And (rc.lpc_phone)<>" "
And (rc.lpc_phone) Not Like "#,*")
AND ((rc.call_mode)="AN")
AND ((rc.row_status)=3
Or (rc.row_status)=11));
MaxDates Query:
SELECT
config_recorder.recorder_id,
Max(config_recorder.trans_datetime) AS MaxOftrans_datetime
FROM config_recorder
GROUP BY config_recorder.recorder_id;
Code used with the sub-query:
SELECT
a.cycle AS cyc,
a.office_id AS ofid,
rc.recorder_id AS recorder,
a.cust_name,
r.account_id,
rc.lpc_phone,
rc.device_serial,
rc.device_mfg,
rc.device_type
INTO Test
FROM config_recorder AS rc, status AS s, recorders AS r, accounts AS a
WHERE rc.recorder_id=r.recorder_id
AND r.account_id=a.account_id
AND ((rc.lpc_phone Is Not Null)
AND (rc.lpc_phone<>" ")
AND (rc.lpc_phone Not Like "#,*"))
AND ((rc.call_mode="AN")
AND (rc.row_status=s.status)
AND ((rc.row_status="3")
Or (rc.row_status="11")))
AND (rc.trans_datetime=(select max(r2.trans_datetime) from config_recorder r2 where r2.recorder_id = rc.recorder_id));
Thank you for your help.

You can consider changing the sub-query into an inline view and then joining it with other tables, as below:
SELECT
a.office_id AS ofid,
rc.recorder_id AS recorder,
a.cust_name,
r.account_id,
rc.lpc_phone,
rc.device_serial,
rc.device_mfg,
rc.device_type
INTO RDS_INFO
FROM status AS s
INNER JOIN
(config_recorder AS rc INNER JOIN recorders AS r ON rc.recorder_id = r.recorder_id)
ON s.status = rc.row_status
INNER JOIN accounts AS a
ON r.account_id = a.account_id
INNER JOIN
(SELECT
config_recorder.recorder_id,
Max(config_recorder.trans_datetime) AS MaxOftrans_datetime
FROM config_recorder
GROUP BY config_recorder.recorder_id) MaxDates
ON (MaxDates.MaxOftrans_datetime = rc.trans_datetime) AND (MaxDates.recorder_id = rc.recorder_id)
WHERE (((rc.lpc_phone) Is Not Null
And (rc.lpc_phone)<>" "
And (rc.lpc_phone) Not Like "#,*")
AND ((rc.call_mode)="AN")
AND ((rc.row_status)=3
Or (rc.row_status)=11));
References:
A related question on SO
Inline Views Versus Temp Tables on MSDN Magazine

Related

SQL- basic but still finding it complicated

I am working on SQL query which should return the list of Managers and the staff who reports to them.
Unfortunately there is no separate table for Employee or Staff but a single 'resource' table called ahsresources.
The managers are identified with a relation called 'C0'.
Even after trying various Joins, I am unable to extract the list. The idea is that a manager will run the report to see his reportees, as well as those staff who report to his own reportees
Example -
Now, if lets say HDY is running the query, then its should return him the below result
Below is the query I have created, but for the matter of understanding the issue, you can use the above example.
select a.description as manager1,a.rel_value as MGID,a.resource_id as Reportee1_MGR2,r.name,a.date_to as date, r.date_to,a1.resource_id as MG3ID,r1.name as Rep3Name,
a2.resource_id as MG4ID,r2.name as Rep4Name
from ahsrelvalue a
LEFT OUTER JOIN ahsresources r
ON r.resource_id = a.resource_id and r.client = a.client and a.date_to='12/31/2099'
LEFT OUTER JOIN ahsrelvalue a1
ON a1.rel_Value = a.resource_id and a1.client = a.client and a1.date_to = '12/31/2099'
LEFT OUTER JOIN ahsrelvalue a2
ON a2.rel_Value = a1.resource_id and a2.client = a1.client and a2.date_to = '12/31/2099'
LEFT OUTER JOIN ahsresources r1
ON r1.resource_id = a1.resource_id and r1.client = a1.client and a1.date_to='12/31/2099'
LEFT OUTER JOIN ahsresources r2
ON r2.resource_id = a2.resource_id and r2.client = a2.client and a2.date_to='12/31/2099'
where a.rel_Value = '$?resid' and a.rel_attr_id='C0' and r.date_to = '12/31/2099' and r1.date_to ='12/31/2099'
and r.status !='C' and r1.status!='C' and r2.status!='C'
In SQL Server, you can use a recursive query to traverse this hierarchical dataset:
with cte as (
select t.* from mytable where managerID = 6
union all
select t.*
from cte c
inner join mytable t on t.managerID = c.staffID
)
select * from cte

How to count unique events in Access?

I am trying to count the unique number of events in the E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID field using count(distinct(E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID)), but I get an error when performing the following query in MS Access 2016.
How to count unique events in the E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID field?
My query in MS Access 2016 (without distinct):
SELECT CUSTOMERS.E_CUST_LOGIN_ID, Count(E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID)
FROM ((((CUSTOMERS
INNER JOIN E_CUSTOMER_DETAIL ON CUSTOMERS.WH_CUST_NO = E_CUSTOMER_DETAIL.WH_CUST_NO)
INNER JOIN E_CUST_E_CUST_REL ON CUSTOMERS.E_CUST_LOGIN_ID = E_CUST_E_CUST_REL.E_CUST_LOGIN_ID)
INNER JOIN E_CUST_CHNL_USAGE_DETAIL ON E_CUST_E_CUST_REL.E_CUST_JOINT_OWN_LOGIN_ID = E_CUST_CHNL_USAGE_DETAIL.E_CUST_LOGIN_ID)
INNER JOIN E_CUSTOMER_DETAIL AS E_CUSTOMER_DETAIL_1 ON E_CUST_E_CUST_REL.E_CUST_JOINT_OWN_LOGIN_ID = E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID)
GROUP BY CUSTOMERS.E_CUST_LOGIN_ID;
MS Access does not support count(distinct). One way to work around this limitation is to select distinct in a subquery, then aggregate:
SELECT LOGIN_ID, Count(DETAIL_LOGIN_ID)
FROM (
SELECT DISTINCT CUSTOMERS.E_CUST_LOGIN_ID AS LOGIN_ID, E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID AS DETAIL_LOGIN_ID
FROM ((((CUSTOMERS
INNER JOIN E_CUSTOMER_DETAIL ON CUSTOMERS.WH_CUST_NO = E_CUSTOMER_DETAIL.WH_CUST_NO)
INNER JOIN E_CUST_E_CUST_REL ON CUSTOMERS.E_CUST_LOGIN_ID = E_CUST_E_CUST_REL.E_CUST_LOGIN_ID)
INNER JOIN E_CUST_CHNL_USAGE_DETAIL ON E_CUST_E_CUST_REL.E_CUST_JOINT_OWN_LOGIN_ID = E_CUST_CHNL_USAGE_DETAIL.E_CUST_LOGIN_ID)
INNER JOIN E_CUSTOMER_DETAIL AS E_CUSTOMER_DETAIL_1 ON E_CUST_E_CUST_REL.E_CUST_JOINT_OWN_LOGIN_ID = E_CUSTOMER_DETAIL_1.E_CUST_LOGIN_ID)
)
GROUP BY LOGIN_ID;

How to Distinct a sql query but still return all columns

This is my query:
SELECT dbo.Webs.Id, dbo.Webs.Title, dbo.Webs.FullUrl, dbo.Roles.RoleId,
dbo.Roles.Title AS RoleTitle, dbo.UserInfo.tp_Title, dbo.UserInfo.tp_Login
FROM dbo.RoleAssignment
INNER JOIN dbo.Roles ON dbo.RoleAssignment.SiteId = dbo.Roles.SiteId
AND dbo.RoleAssignment.RoleId = dbo.Roles.RoleId
INNER JOIN dbo.Webs ON dbo.Roles.SiteId = dbo.Webs.SiteId
AND dbo.Roles.WebId = dbo.Webs.Id
INNER JOIN dbo.UserInfo ON dbo.RoleAssignment.PrincipalId = dbo.UserInfo.tp_ID
WHERE tp_Title = 'HOBSON, Will';
This database contains all the permissions for the users of all sharepoint sites. I'm trying to create a query that displays all sites the user has access to. Currently it outputs a lot of duplicate information. I only want it to display results that have either a distinct Role Title or a distinct Web id.
So for example, in this query I would only want to see 4 results; 1, 5, 11 and 13.
(all this information is on a local test SharePoint installation that cannot be accessed externally, so the only information I'm giving away here is my name :))
Your query would be much easier to read with table aliases. The direct answer to your question is to use SELECT DISTINCT:
SELECT DISTINCT w.Id, w.Title, w.FullUrl, r.RoleId, r.Title AS RoleTitle,
ui.tp_Title, ui.tp_Login
FROM dbo.RoleAssignment ra INNER JOIN
dbo.Roles r
ON ra.SiteId = r.SiteId AND
ra.RoleId = r.RoleId INNER JOIN
dbo.Webs w
ON r.SiteId = w.SiteId AND
r.WebId = w.Id INNER JOIN
dbo.UserInfo ui
ON ra.PrincipalId = ui.tp_ID
WHERE tp_Title = 'HOBSON, Will';
However, it would be better to find the cause of the duplicates. Often duplicates like this are caused by incomplete join conditions. Fixing the join is the better approach, but sometimes SELECT DISTINCT is necessary.
You can just add a DISTINCT to your query:
SELECT DISTINCT dbo.Webs.Id, dbo.Webs.Title, dbo.Webs.FullUrl, dbo.Roles.RoleId,
dbo.Roles.Title AS RoleTitle, dbo.UserInfo.tp_Title, dbo.UserInfo.tp_Login
FROM dbo.RoleAssignment
INNER JOIN dbo.Roles ON dbo.RoleAssignment.SiteId = dbo.Roles.SiteId
AND dbo.RoleAssignment.RoleId = dbo.Roles.RoleId
INNER JOIN dbo.Webs ON dbo.Roles.SiteId = dbo.Webs.SiteId
AND dbo.Roles.WebId = dbo.Webs.Id
INNER JOIN dbo.UserInfo ON dbo.RoleAssignment.PrincipalId = dbo.UserInfo.tp_ID
WHERE tp_Title = 'HOBSON, Will';

Creating INNER Joins with IIF

I am trying to join three tables using and inner join and have the contents of one change colour if an e-mail has been sent.
Below is my query
SELECT IIF(COUNT Holdsent.job)>0, #STD, #RED) AS Colour, jobs.job, jobs.jobstatus, jobs.client, jobs.logdate
FROM jobs INNER JOIN clients ON clients.client = jobs.client INNER JOIN holdsent ON holdsent.job = jobs.job
WHERE (jobs.jobstatus = 'HOLD' OR jobs.jobstatus = 'CLIHOLD')
Below is the error I receive
Expected lexical element not found: (missing ( in aggregate function
[Parsing Expression (column1 in the SELECT clause)] -- Location of
error in the SQL statement is:1 SELECT IIF(COUNT
Holdsent.job)>0,#STD, #RED) AS COLOUR,jobs.job,
jobs.jobstatus,jobs.client,jobs.logdate FROM jobs INNER JOIN clients
ON clients.client = jobs.client INNER JOIN holdsent ON holdsent.job =
jobs.job WHERE (jobs.jobstatus = 'HOLD' OR jobs.jobstatus =
'CLIHOLD')
I am new to SQL and can do basic queries, but am not clear on IIF. Thank you in advance for any help you can provide.
iif is a special function from MS Access, which SQL Server has started supporting with the most recent version. The correct SQL form is the case statement. An improved version of your query, written in standard SQL, is:
SELECT (case when COUNT(hs.job)>0 then #STD else #RED end) AS Colour,
j.job, j.jobstatus, j.client, j.logdate
FROM jobs j INNER JOIN
clients c
ON c.client = j.client INNER JOIN
holdsent hs
ON hs.job = j.job
WHERE j.jobstatus in ('HOLD', 'CLIHOLD')
Sorry, I didnt mock up test tables for this, but I think you want to subquery against your holdsent table to get the count value into the main query, then use three parameters in your IIF() function. I didnt make a test case, but I think this will suit your needs:
SELECT IIF((isnull(holdsentCount.jobCount,0)>0, #STD, #RED) AS Colour
, jobs.job
, jobs.jobstatus
, jobs.client
, jobs.logdate
FROM jobs
JOIN clients
ON clients.client = jobs.client
LEFT JOIN (select holdsent.job
, count(*) as jobCount
from holdsent
group by holdsent.job ) as holdsentCount
ON holdsentCount.job = jobs.job
WHERE (jobs.jobstatus = 'HOLD' OR jobs.jobstatus = 'CLIHOLD')
Also, wrap the holdsentCount.jobCount with isnull() so that it returns 0 if it isn't returned in the subquery.
EDIT: I dont know what '#STD' or '#RED' are so I left them as-is.

Complex SQL Query to NHibernate DetachedCriteria or HQL

I have the following SQL Query returning the results I need:
SELECT
Person.FirstName,Person.LastName,OrganisationUnit.Name AS UnitName, RS_SkillsArea.Name AS SkillsArea, Activity.Name AS ActivityName, Activity.CLASS, Activity.StartsOn, Activity.EndsOn,
SUM(ActivityCost.CostAmount) /
NULLIF(
(
SELECT COUNT(Registration.ActivityId) FROM
Registration INNER JOIN AttemptResultsSummary ON Registration.CurrentResultId = AttemptResultsSummary.AttemptResultsSummaryId AND
Registration.RegistrationId = AttemptResultsSummary.RegistrationId
WHERE (Registration.Status = 1) AND (Registration.ActivityId = Activity.ActivityId)
AND (AttemptResultsSummary.AttendanceStatus <> 1)
)
,0)
AS IndividualCost
FROM Registration AS Registration_1 INNER JOIN
Activity ON Registration_1.ActivityId = Activity.ActivityId INNER JOIN
Person ON Registration_1.PersonId = Person.PersonId INNER JOIN
OrganisationUnit ON Person.OrganisationUnitId = OrganisationUnit.OrganisationUnitId INNER JOIN
AttemptResultsSummary ON Registration_1.CurrentResultId = AttemptResultsSummary.AttemptResultsSummaryId AND
Registration_1.RegistrationId = AttemptResultsSummary.RegistrationId AND Activity.ActivityId = AttemptResultsSummary.ActivityId AND
Person.PersonId = AttemptResultsSummary.PersonId INNER JOIN
ActivityCost ON Activity.ActivityId = ActivityCost.ActivityId LEFT OUTER JOIN
(SELECT Category.Name, Category.CategoryId
FROM Category INNER JOIN
CategoryGroup ON Category.[Group] = CategoryGroup.CategoryGroupId
WHERE (CategoryGroup.Name = N'Skills Area')) AS RS_SkillsArea INNER JOIN
ActivityInCategory ON RS_SkillsArea.CategoryId = ActivityInCategory.CategoryId ON Activity.ActivityId = ActivityInCategory.ActivityId
AND AttemptResultsSummary.AttendanceStatus <> 1
GROUP BY RS_SkillsArea.Name, Person.FirstName,Person.LastName,Activity.Name, Activity.CLASS, Activity.StartsOn, Activity.EndsOn, Activity.ActivityId, OrganisationUnit.Name,
AttemptResultsSummary.CompletionStatus, AttemptResultsSummary.AttendanceStatus
HAVING AttemptResultsSummary.AttendanceStatus <> 1
Essentially is there any way using either DetachedCriteria or HQL to do the same against the entities rather than direct SQL?
The two challenges are:
The query for cost calculation per row.
The derived table join (which needs to be an outer join as this value may not exist)
I'd appreciate any pointers. I'd rather not use SQL because of infrastructure changes and the issues with (lack of) refactoring support
Take a look at the official HQL examples # http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql.html#queryhql-examples .
In my opinion, the 'derived joins' would be even easier to pull off using HQL.
In the case of performance, my first start would be to catch how much it costs using native SQL using your prefered profiler, and then how much it costs on NHibernate using NHProf.