How to add in a Count in the sub Query - sql

I have to count in the sub query. I want to add this to the subquery.
I am getting a fail when I add this line to the sub query:
SELECT COUNT(DISTINCT v.Department_descr), v.Patient_Number, b.patient_name FROM vwGenVouchInfo v) as pat_dept
SELECT *
FROM vwGenVouchInfo v
WHERE v.Patient_Number=b.Patient_Number
AND v.Voucher_Primary_Diagnosis_Code IN ('Z00.129', 'Z00.00') )
ORDER BY voucher_primary_diagnosis_code
I want to get a result that will have the patient which department they went to the most times. I am not sure how to add this in. I want pat_dept to hold the dept the patient went to the most times besides the dental dept.
SELECT distinct
a.voucher_primary_diagnosis_code,
a.Patient_Number
, b.Patient_Name, b.patient_home_phone, patient_age
FROM vwGenVouchInfo a
LEFT JOIN vwGenPatInfo b ON a.Patient_Number=b.Patient_Number
WHERE
a.Department_Descr = 'Dental'
and a.Voucher_Service_Date >= '2015-01-01'
AND NOT EXISTS (
-- This subquery looks at other vouchers of the same patient.
--
SELECT *
FROM vwGenVouchInfo v
WHERE v.Patient_Number=b.Patient_Number
AND v.Voucher_Primary_Diagnosis_Code IN ('Z00.129', 'Z00.00') )
ORDER BY voucher_primary_diagnosis_code

If you need this in the subquery, you will have to put all the necessary columns one by one along with the count and then group by the same columns.

Related

Top nth of a report with rest as other

I have a query that shows total amount by supplier. I want a report where the top 9 are shown and the rest is added together under an "other" supplier. Thus the detail shows top 9 suppliers, other groups the rest and the total on the report includes all.
I cant get the top 9 but how to I get the "other"
I use MS Access 2007
EDIT:
I also need to add a company name that is in another register. t_costed has the value, linked to t_register_2bre that has the company, linked to t_contacts_company that had the company name.
I know the amount of columns returned by the select query must be equal in the two unioned queries but I'm struggling with the INNER JOIN.
You could use a union query in the following way:
select top 9 t.amount
from table1 t
order by t.amount desc
union all
select sum(u.amount)
from table1 u
where
u.amount <
(
select min(v.amount)
from
(select top 9 w.amount from table1 w order by w.amount desc) v
)
Here, change all references to table1 with the name of your table.
EDIT:
With the additional information provided in your question, I would suggest the following (untested) SQL:
select top 9 c.company, t.amount
from
(t_costed t inner join t_register_2bre r on t.eventidbre = r.id_bre)
inner join t_contacts_company c on r.defaulting_partyid = c.id_contacts_company
order by t.amount desc
union all
select "Others" as company, sum(u.amount)
from t_costed u
where
u.amount <
(
select min(v.amount)
from
(select top 9 w.amount from t_costed w order by w.amount desc) v
)

SUM a column count from two tables

I have this simple unioned query in SQL Server 2014 where I am getting counts of rows from each table, and then trying to add a TOTAL row at the bottom that will SUM the counts from both tables. I believe the problem is the LEFT OUTER JOIN on the last union seems to be only summing the totals from the first table
SELECT A.TEST_CODE, B.DIVISION, COUNT(*)
FROM ALL_USERS B, SIGMA_TEST A
WHERE B.DOMID = A.DOMID
GROUP BY A.TEST_CODE, B.DIVISION
UNION
SELECT E.TEST_CODE, F.DIVISION, COUNT(*)
FROM BETA_TEST E, ALL_USERS F
WHERE E.DOMID = F.DOMID
GROUP BY E.TEST_CODE, F.DIVISION
UNION
SELECT 'TOTAL', '', COUNT(*)
FROM (SIGMA_TEST A LEFT OUTER JOIN BETA_TEST E ON A.DOMID
= E.DOMID )
Here is a sample of the results I am getting:
I would expect the TOTAL row to display a result of 6 (2+1+3=6)
I would like to avoid using a Common Table Expression (CTE) if possible. Thanks in advance!
Since you are counting users with matching DOMIDs in the first two statements, the final statement also needs to include the ALL_USERS table. The final statement should be:
SELECT 'TOTAL', '', COUNT(*)
FROM ALL_USERS G LEFT OUTER JOIN
SIGMA_TEST H ON G.DOMID = H.DOMID
LEFT OUTER JOIN BETA_TEST I ON I.DOMID = G.DOMID
WHERE (H.TEST_CODE IS NOT NULL OR I.TEST_CODE IS NOT NULL)
I would consider doing a UNION ALL first then COUNT:
SELECT COALESCE(TEST_CODE, 'TOTAL'),
DIVISION,
COUNT(*)
FROM (
SELECT A.TEST_CODE, B.DIVISION
FROM ALL_USERS B
INNER JOIN SIGMA_TEST A ON B.DOMID = A.DOMID
UNION ALL
SELECT E.TEST_CODE, F.DIVISION
FROM BETA_TEST E
INNER JOIN ALL_USERS F ON E.DOMID = F.DOMID ) AS T
GROUP BY GROUPING SETS ((TEST_CODE, DIVISION ), ())
Using GROUPING SETS you can easily get the total, so there is no need to add a third subquery.
Note: I assume you want just one count per (TEST_CODE, DIVISION). Otherwise you have to also group on the source table as well, as in #Gareth's answer.
I think you can achieve this with a single query. It seems your test tables have similar structures, so you can union them together and join to ALL_USERS, finally, you can use GROUPING SETS to get the total
SELECT ISNULL(T.TEST_CODE, 'TOTAL') AS TEST_CODE,
ISNULL(U.DIVISION, '') AS DIVISION,
COUNT(*)
FROM ALL_USERS AS U
INNER JOIN
( SELECT DOMID, TEST_CODE, 'SIGNMA' AS SOURCETABLE
FROM SIGMA_TEST
UNION ALL
SELECT DOMID, TEST_CODE, 'BETA' AS SOURCETABLE
FROM BETA_TEST
) AS T
ON T.DOMID = U.DOMID
GROUP BY GROUPING SETS ((T.TEST_CODE, U.DIVISION, T.SOURCETABLE), ());
As an aside, the implicit join syntax you are using was replaced over a quarter of a century ago in ANSI 92. It is not wrong, but there seems to be little reason to continue to use it, especially when you are mixing and matching with explicit outer joins and implicit inner joins. Anyone else that might read your SQL will certainly appreciate consistency.

Slow MS Access Sub Query

I have three tables in Access:
employees
----------------------------------
id (pk),name
times
----------------------
id (pk),employee_id,event_time
time_notes
----------------------
id (pk),time_id,note
I want to get the record for each employee record from the times table with an event_time immediately prior to some time. Doing that is simple enough with this:
select employees.id, employees.name,
(select top 1 times.id from times where times.employee_id=employees.id and times.event_time<=#2018-01-30 14:21:48# ORDER BY times.event_time DESC) as time_id
from employees
However, I also want to get some indication of whether there's a matching record in the time_notes table:
select employees.id, employees.name,
(select top 1 time_notes.id from time_notes where time_notes.time_id=(select top 1 times.id from times where times.employee_id=employees.id and times.event_time<=#2018-01-30 14:21:48# ORDER BY times.event_time DESC)) as time_note_present,
(select top 1 times.id from times where times.employee_id=employees.id and times.event_time<=#2018-01-30 14:21:48# ORDER BY times.event_time DESC) as last_time_id
from employees
This does work but it's SOOOOO SLOW. We're talking 10 seconds or more if there's 100 records in the employee table. The problem is peculiar to Access as I can't use the last_time_id result of the other sub-query like I can in MySQL or SQL Server.
I am looking for tips on how to speed this up. Either a different query, indexes. Something.
Not sure if something like this would work for you?
SELECT
employees.id,
employees.name,
time_notes.id AS time_note_present,
times.id AS last_time_id
FROM
(
employees LEFT JOIN
(
times INNER JOIN
(
SELECT times.employee_id AS lt_employee_id, max(times.event_time) AS lt_event_time
FROM times
WHERE times.event_time <= #2018-01-30 14:21:48#
GROUP BY times.employee_id
)
AS last_times
ON times.event_time = last_times.lt_event_time AND times.employee_id = last_times.lt_employee_id
)
ON employees.id = times.employee_id
)
LEFT JOIN time_notes ON times.id = time_notes.time_id;
(Completely untested and may contain typos)
Basically, your query is running multiple correlated subqueries even a nested one in a WHERE clause. Correlated queries calculate a value separately for each row, corresponding to outer query.
Similar to #LeeMac, simply join all your tables to an aggregate query for the max event_time grouped by employee_id which will run once across all rows. Below times is the baseFROM table joined to the aggregate query, employees, and time_notes tables:
select e.id, e.name, t.event_time, n.note
from ((times t
inner join
(select sub.employee_id, max(sub.event_time) as max_event_time
from times sub
where sub.event_time <= #2018-01-30 14:21:48#
group by sub.employee_id
) as agg_qry
on t.employee_id = agg_qry.employee_id and t.event_time = agg_qry.max_event_time)
inner join employees e
on e.id = t.employee_id)
left join time_notes n
on n.time_id = t.id

NOT IN not working sufficient

I wrote this to get me the patients who have never had the diagnosis codes stated. But I am getting results of people who DID. Only that it's NOT displaying that codes in the output but other codes they have.
But I need to not include the patient who has that code but the NOT IN part
is not working or what else am I missing?
select Distinct a.voucher_primary_diagnosis_code, a.Patient_Number, b.Patient_Name vwGenVouchInfo a
left join vwGenPatInfo b on a.Patient_Number=b.patient_number
where
a.Department_Descr = 'Pediatrics' and Actual_Dr_ID <> 6 and Voucher_Primary_Diagnosis_Code not in
('Z00.129', 'Z00.00') order by patient_name
NOT IN condition applies to a single row. However, you are looking for non-existence of any rows associated with the given patient that match the specified codes, so you need a different condition: you need to say that for a given patient there's no diagnosis code on the specified list. For example, you could use EXISTS quantifier for that:
SELECT
a.voucher_primary_diagnosis_code
, a.Patient_Number
, b.Patient_Name
FROM vwGenVouchInfo a
LEFT JOIN vwGenPatInfo b ON a.Patient_Number=b.Patient_Number
WHERE
a.Department_Descr = 'Pediatrics'
AND Actual_Dr_ID <> 6
AND NOT EXISTS (
-- This subquery looks at other vouchers of the same patient.
-- If any of them has Diagnosis Code from the prohibited list,
-- all records for that patient are rejected.
SELECT *
FROM vwGenVouchInfo v
WHERE v.Patient_Number=b.Patient_Number
AND v.Voucher_Primary_Diagnosis_Code IN ('Z00.129', 'Z00.00')
)
ORDER BY Patient_Name
You can do this with a left join. Here is one way to get the patients who meet your description:
select p.Patient_Number, p.Patient_Name
from vwGenPatInfo p left join
vwGenVouchInfo v
on p.Patient_Number = v.patient_number and
v.Department_Descr = 'Pediatrics' and
v.Actual_Dr_ID <> 6 and
v.Voucher_Primary_Diagnosis_Code in ('Z00.129', 'Z00.00')
where v.patient_number is null
order by p.patient_n
Note this doesn't return information about diagnoses, because you are getting the patients who don't have the diagnoses you care about.

How to get the value of max() group when in subquery?

So i woud like to find the department name or department id(dpmid) for the group that has the max average of age among the other group and this is my query:
select
MAX(avg_age) as 'Max average age' FROM (
SELECT
AVG(userage) AS avg_age FROM user_data GROUP BY
(select dpmid from department_branch where
(select dpmbid from user_department_branch where
user_data.userid = user_department_branch.userid)=department_branch.dpmbid)
) AS query1
this code show only the max value of average age and when i try to show the name of the group it will show the wrong group name.
So, How to show the name of max group that has subquery from another table???
You may try this..
select MAX(avg_age) as max_avg, SUBSTRING_INDEX(MAX(avg_age_dep),'##',-1) as max_age_dep from
(
SELECT
AVG(userage) as avg_age, CONCAT( AVG(userage), CONCAT('##' ,department_name)) as avg_age_dep
FROM user_data
inner join user_department_branch
on user_data.userid = user_department_branch.userid
inner join department_branch
on department_branch.dpmbid = user_department_branch.dpmbid
inner join department
on department.dpmid = department_branch.dpmid
group by department_branch.dpmid
) tab_avg_age_by_dep
;
I've done some change on ipothesys that the department name is placed in a "department" anagraphical table.. so, as it needed put in join a table in plus, then I changed your query, eventually if the department name is placed (but I don't thing so) in the branch_department table you can add the field and its treatment to your query
update
In adjunct to as said, if you wanto to avoid identical average cases you can furtherly make univocal the averages by appending a rownum id in this way:
select MAX(avg_age) as max_avg, SUBSTRING_INDEX(MAX(avg_age_dep),'##',-1) as max_age_dep from
(
SELECT
AVG(userage) as avg_age, CONCAT( AVG(userage), CONCAT('##', CONCAT( #rownum:=#rownum+1, CONCAT('##' ,department_name)))) as avg_age_dep
FROM user_data
inner join user_department_branch
on user_data.userid = user_department_branch.userid
inner join department_branch
on department_branch.dpmbid = user_department_branch.dpmbid
inner join department
on department.dpmid = department_branch.dpmid
,(SELECT #rownum:=0) r
group by department_branch.dpmid
) tab_avg_age_by_dep
;
I took a shot at what I think you are looking for. The following will give you the department branch with the highest average age. I assumed the department_branch table had a department_name field. You may need an additional join to get the department.
SELECT db.department_name, udb.dpmid, AVG(userage) as `Average age`
FROM user_data as ud
JOIN user_department_branch as udb
ON udb.userid = ud.userid
JOIN department_branch as db
ON db.dpmbid = udb.dpmbid
GROUP BY udb.dpmid
ORDER BY `Average age` DESC
LIMIT 1