I have a table of foods (FOODS) and a table of food groups (FOOD_GROUP). I have build a list to summarize available food count for each group based on the search term entered by the user:
SELECT FOOD_GROUP.FoodGroupID, FOOD_GROUP.FoodGroupName, Count(FOODS.FoodID) AS CntOfFoodID
FROM FOOD_GROUP LEFT JOIN FOODS ON FOOD_GROUP.FoodGroupID = FOODS.FoodGroupID
WHERE (((FOODS.FoodName) Like ("*" & [Forms]![FoodSearch]![search_term] & "*")))
GROUP BY FOOD_GROUP.FoodGroupID, FOOD_GROUP.FoodGroupName
ORDER BY FOOD_GROUP.FoodGroupName;
But now it gets tricky. I want to add a summary row at the top that list. Here is a nearly simmilar example:
I guess I must do something like:
SELECT '0000', 'Show all foods', Count(FOODS.FoodID) AS CntOfFoodID
FROM FOODS
WHERE (((FOODS.FoodName) Like ("*" & [Forms]![FoodSearch]![search_term] & "*")))
UNION
SELECT FOOD_GROUP.FoodGroupID, FOOD_GROUP.FoodGroupName, Count(FOODS.FoodID) AS CntOfFoodID
FROM FOOD_GROUP LEFT JOIN FOODS ON FOOD_GROUP.FoodGroupID = FOODS.FoodGroupID
WHERE (((FOODS.FoodName) Like ("*" & [Forms]![FoodSearch]![search_term] & "*")))
GROUP BY FOOD_GROUP.FoodGroupID, FOOD_GROUP.FoodGroupName
ORDER BY FOOD_GROUP.FoodGroupName;
But GROUP BY fails and if I take out GROUP BY than FoodGroupID is missing.
Any hints?
I think this should work:
SELECT '0000', 'Show all foods', Count(f.FoodID) AS CntOfFoodID
FROM FOODS as f
WHERE f.FoodName Like "*" & [Forms]![FoodSearch]![search_term] & "*"
UNION ALL
SELECT fg.FoodGroupID, fg.FoodGroupName, Count(f.FoodID) AS CntOfFoodID
FROM FOOD_GROUP fg LEFT JOIN
FOODS as f
ON fg.FoodGroupID = f.FoodGroupID
WHERE f.FoodName Like "*" & [Forms]![FoodSearch]![search_term] & "*"
GROUP BY fg.FoodGroupID, fg.FoodGroupName
ORDER BY 1;
The changes are:
Use UNION instead of UNION ALL. This is more efficient.
The ORDER BY does not refer to a column name (especially one only in the second subquery).
The use of table aliases just simplifies the query; it doesn't affect the logic or performance.
Related
I have used SqlDataSource and have a select query based on District & Zone as below
SELECT a.[committee_id] memberid, a.[membername], a.[memberemail], a.[memberdesignation], a.[membercreatedby],b.districtname AS district,b.districtid,c.zone_name AS zone,c.zoneid
FROM [committee_details] a
LEFT JOIN district_master b on b.districtid=a.districtid
LEFT JOIN zone_master c on c.districtid=a.districtid and c.zoneid = a.zoneid
WHERE (a.[membercreatedby] = 'director') AND ((convert(varchar,a.districtid) LIKE '%2%') AND (convert(varchar,a.zoneid) LIKE '%25%')) ORDER BY a.[committee_id] DESC
It's an inline query. I have tried above query but not able to figure out how to Select condition based.
I want if district supplied then Select according to District, if both District & Zone supplied then Select according to both & If nothing supplied then Select All. But should be in single query. How should I do this?
First, fix your query so you are not using meaningless table aliases. Use table abbreviations! I would also drop all the square braces; they just make the query harder to write and to read.
Basically, you want comparisons with NULL in the WHERE clause. I have no idea why your sample code uses LIKE, particularly columns that appear to be numbers. Nothing in the question explains why LIKE is used for the comparison, so the idea is:
SELECT cd.committee_id as memberid, cd.membername,
cd.memberemail, cd.memberdesignation, cd.membercreatedby,
dm.districtname AS district, dm.districtid,
zm.zone_name AS zone, zm.zoneid
FROM committee_details cd LEFT JOIN
district_master dm
ON cd.districtid = dm.districtid LEFT JOIN
zone_master zm
ON zm.districtid = cd.districtid AND
zm.zoneid = cd.zoneid
WHERE cd.membercreatedby = 'director') AND
(cd.districtid = #district or #district is null) AND
(cd.zoneid = #zone or #zone is null)
ORDER BY cd.[committee_id] DESC;
If you were using LIKE, then I would phrase the logic like:
WHERE cd.membercreatedby = 'director') AND
(cast(cd.districtid as varchar(255)) like #district) AND
(cast(cd.zoneid as varchar(255)) like #zone)
And pass in the patterns as '%' when you want all values to match. This assumes that the columns in cd are not NULL. If they can be NULL, then you want an explicit comparison, as in the first example.
If I got the question right then you can use parameters and compare to the column itself if the values are not supplied or not present.
try the following:
SELECT a.[committee_id] memberid, a.[membername], a.[memberemail], a.[memberdesignation], a.[membercreatedby],b.districtname AS district,b.districtid,c.zone_name AS zone,c.zoneid
FROM [committee_details] a
LEFT JOIN district_master b on b.districtid=a.districtid
LEFT JOIN zone_master c on c.districtid=a.districtid and c.zoneid = a.zoneid
WHERE (a.[membercreatedby] = 'director')
AND b.districtname = isnull(nullif(#districtname, ''), b.districtname)
AND c.zone_name = isnull(nullif(#zone_name, ''), c.zone_name)
ORDER BY a.[committee_id] DESC
I need to add a column with the content of this query :
SELECT COUNT(*) FROM account_subscriptiongroups WHERE account_subscriptiongroups.active = true AND account_subscriptiongroups.user_id = account_user.id
to this query :
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
LEFT JOIN
account_subscriptiongroup ON account_adminaction.sub_group_id = account_subscriptiongroup.id
WHERE
account_adminaction.created_on >= '2021-04-07' AND account_adminaction.created_on <= '2021-04-13' AND
((account_adminaction.description LIKE 'Arrêt de l''abonnement%') OR (account_adminaction.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY
subscription_ended_on
I tried adding a LEFT JOIN like that:
LEFT JOIN
account_subscriptiongroup all_sg ON account_user.id = account_subscriptiongroup.user_id
with this line in my WHERE statement :
AND all_sg.active = true
and this line in my SELECT :
COUNT(all_sg.id)
but I get an error :
ERROR: column "account_user.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 2: account_user.id as user_id, account_user.email, account_us...
^
I don't understand how I could perform this action properly
To count something, you need to specify a group where that count applies.
So every column that you select (and is not used in an aggregate function, like COUNT or SUM), you need to mention in the GROUP BY clause.
Or to put it the other way around: the non-aggregate columns must apply to all rows that are contained in that particular COUNT.
So between the WHERE and ORDER BY clauses, add a GROUP BY clause:
GROUP BY account_user.id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id,
account_adminaction.description,
account_adminaction.id,
account_adminaction.created_on
If, on the other hand, you want a count from a different table, you can add a sub-select:
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on,
(SELECT COUNT(*)
FROM account_subscriptiongroups
WHERE account_subscriptiongroups.active = true
AND account_subscriptiongroups.user_id = account_user.id) AS groupcount
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
You can left join to to a derived table that does the grouping and counting:
SELECT au.id as user_id, au.email, au.first_name, au.last_name, au.phone,
asg.id as sub_group_id,
ad.description,
ad.id as admin_action_id,
ad.created_on as subscription_ended_on,
asgc.num_groups
FROM account_adminaction ad
LEFT JOIN account_user au ON au.id = ad.user_id
LEFT JOIN account_subscriptiongroups asg on ON ad.sub_group_id = asg.id
LEFT JOIN (
SELECT user_id, count(*) as num_groups
FROM account_subscriptiongroups ag
WHERE ag.active
GROUP by user_id
) asgc on asgc.user_id = au.id
WHERE ad.created_on >= '2021-04-07'
AND ad.created_on <= '2021-04-13'
AND ((ad.description LIKE 'Arrêt de l''abonnement%') OR (ad.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY subscription_ended_on
It's not entirely clear to me, what you are trying to count, but another option (most probably slower) could be to use a window function combined with a filter clause:
count(*) filter (where asg.active) over (partition by asg.user_id) as num_groups
EDIT: my answer is the same as submitted by a_horse_with_no_name
Two answers, a literal one just solving the problem you posed, and then another one questioning whether what you asked for is really what you want.
Simple answer: modify your desired query to add user_id to the Select and remove user_id from the Where clause. Now you have a table that can be left-joined to the rest of your larger query.
...
Left Join (Select user_id, count(*) as total_count
From account_subscriptiongroup
Where account_subscriptiongroups.active = true
Group By user_id) Z
On Z.user_id=account_user.id
I question whether this count is what you really want here. This counts every account_subscriptiongroup entry for all time but only the active ones. Your larger query brings back inactive as well as active records, so if your goal is to create a denominator for a percentage, you are mixing 'apples and oranges' here.
If you decide you want a total by user of the records in your query instead, then you can add one more element to your larger query without adding any more tables. Use a windowing function like this:
Select ..., Sum(Case When account_subscriptiongroup.active Then 1 else 0 End) Over (Group By account_user.id) as total count
This just counts the records within the date range and having the desired actions.
I am trying to write a query to retrieve the max dispatched date and also bringing in fields from multiple tables. Im using the fields from other tables since the table that contains the dispatched date does not contain any of the information that I am looking for. (hope that makes sense) This is the code that I am currently using and it is not working:
Here is what I was getting before I started playing with the code:
This is what I am trying to retrieve:
select a.order_num,
LAB_USER.NAME as ASGN_TECH,
(select max(asn_assignment.dispatched)
from asn_assignment
where a.for_order=asn_assignment.for_order) as "max date"
from asn_assignment, ord_order_state a, lab_user, lab_resource, asn_assignment
where
LAB_USER.USER_ID = LAB_RESOURCE.FOR_USER
AND LAB_RESOURCE.RESOURCE_ID = ASN_ASSIGNMENT.FOR_RESOURCE
and order_num in ('800000194709',
'800000213722',
'800006513931',
'800006525705')
Perhaps fix it like this first while still don't catch you point:
select a.order_num,
LAB_USER.NAME as ASGN_TECH,
(select max(asn_assignment.dispatched)
from asn_assignment
where a.for_order=asn_assignment.for_order) as "max date"
from asn_assignment, ord_order_state a, lab_user, lab_resource
where a.for_order=asn_assignment.for_order
and LAB_USER.USER_ID = LAB_RESOURCE.FOR_USER
AND LAB_RESOURCE.RESOURCE_ID = ASN_ASSIGNMENT.FOR_RESOURCE
and order_num in ('800000194709',
'800000213722',
'800006513931',
'800006525705')
You can rewrite the query through use of DENSE_RANK() analytic function such as
SELECT order_num, asgn_tech, dispatched
FROM
(
SELECT o.order_num,
lu.name as asgn_tech,
a.dispatched,
DENSE_RANK() OVER (PARTITION BY o.order_num
ORDER BY a.dispatched DESC) AS dr
FROM asn_assignment a
JOIN ord_order_state o
ON o.for_order = a.for_order
JOIN lab_resource lr
ON lr.resource_id = a.for_resource
JOIN lab_user lu
ON lu.user_id = lr.for_user
WHERE order_num IN (800000194709, 800000213722, 800006513931, 800006525705)
)
WHERE dr = 1
where
prefer using SQL-92 standard which contains JOIN keyword rather
than former standards in which the tables are seperated by commas
properly alias tables with one or two (maybe three depending on the
tables' names) letters conforming to initial letters of their names or underscore seperated substrings within the names
get rid of quotes wrapping up the values within the list after IN
operator considering data type of order_num is numeric
If your database is 12c+, then you can also prefer using the below query without need of a subquery such as
SELECT o.order_num,
lu.name as asgn_tech,
a.dispatched
FROM asn_assignment a
JOIN ord_order_state o
ON o.for_order = a.for_order
JOIN lab_resource lr
ON lr.resource_id = a.for_resource
JOIN lab_user lu
ON lu.user_id = lr.for_user
WHERE order_num IN (800000194709, 800000213722, 800006513931, 800006525705)
ORDER BY DENSE_RANK() OVER (PARTITION BY o.order_num ORDER BY a.dispatched DESC)
FETCH NEXT 1 ROW WITH TIES
This SQL is returning the record count of table Dims instead returning the record count of table BIDdetails. How can I fix?
BIDReportSearch.CommandText = ("SELECT BIDdetails.Origin, BIDdetails.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(BIDdetails.Origin) as NoOfShpt
FROM BIDdetails LEFT JOIN DIMS ON BidDetails.BID=Dims.BID
where BIDdetails.OrgCountry<>'AE' and BIDdetails.DestCountry='AE' and
BIDdetails.ClosingDate>=#" & dtpBIDfrom.Value & "# and BIDdetails.ClosingDate<=#" &
dtpBIDto.Value & "# GROUP BY BIDdetails.Origin, BIDdetails.Destination
ORDER BY Round(Sum(Dims.ChargeableWeight)) DESC")
The expression:
count(BIDdetails.Origin)
simply counts the number of non-NULL values of BIDdetails.Origin in each group. Because you are actually grouping by the field, that would be the number of rows in each group.
You can get what you want in most databases by using count(distinct) on a unique identifier. Alas, MS Access doesn't support count(distinct) so such a query is much harder to write in Access. You can get just the count field by doing:
SELECT BIDdetails.Origin, BIDdetails.Destination, count(*) as NoOfShpt
FROM BIDdetails
where BIDdetails.OrgCountry <> 'AE' and BIDdetails.DestCountry='AE' and
BIDdetails.ClosingDate>=#" & dtpBIDfrom.Value & "# and BIDdetails.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY BIDdetails.Origin, BIDdetails.Destination;
And then combining the results either in your application or by joining this query back into the original one.
EDIT:
This is your original query:
SELECT d.Origin, d.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(d.Origin) as NoOfShpt
FROM BIDdetails as d LEFT JOIN
DIMS
ON BidDetails.BID=Dims.BID
where d.OrgCountry <> 'AE' and d.DestCountry='AE' and
d.ClosingDate> = #" & d.Value & "# and d.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY d.Origin, d.Destination
ORDER BY Round(Sum(Dims.ChargeableWeight)) DESC
There is another approach, where you aggregate first by the details and then again. I think that is easier in this case:
SELECT Origin, Destination, SUM(CWeight) as CWeight, COUNT(*) as NumShip
FROM (SELECT d.id, d.Origin, d.Destination,
Round(Sum(Dims.ChargeableWeight)) as CWeight, count(d.Origin) as NoOfShpt
FROM BIDdetails as d LEFT JOIN
DIMS
ON BidDetails.BID = Dims.BID
where d.OrgCountry <> 'AE' and d.DestCountry='AE' and
d.ClosingDate> = #" & d.Value & "# and d.ClosingDate<=#" & dtpBIDto.Value & "#
GROUP BY d.id, d.Origin, d.Destination
) as d
GROUP BY Origin, Destination
ORDER BY Round(Sum(CWeight)) DESC;
d.id refers to whatever the unique id is for what you want to count.
I have an Access DB I am maintaining for a client.
I have 4 tables. Claims, Eligibility, Pharmacy, and Codes.
The Primary Key I am using is PHID + SID = MemberID which I am linking to each table, and then the Codes table is merely used for a description. See queries below for better visualization of that...
Query 1: Member_Claims_Query
SELECT
Eligibility.GROUPID,
Eligibility.PHID & '-' & Eligibility.SID AS MemberID,
[Eligibility].[DOB] AS DOB,
Eligibility.GENDER,
Eligibility.RELATIONSHIP_CODE,
MaxDiagDollars.HighestDiagPaid/SUM(Claims.PAID_AMT) AS ['%'],
MaxDiagDollars.HighestDiagPaid/SUM(Claims.PAID_AMT) as 'Percent',
ROUND(SUM(Claims.PAID_AMT)) AS TOTALPAID,
ROUND(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2011',Claims.PAID_AMT,0))) AS 2011TOTALPAID,
ROUND(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2012',Claims.PAID_AMT,0))) AS 2012TOTALPAID,
ROUND(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2013',Claims.PAID_AMT,0))) AS 2013TOTALPAID
FROM (Claims
INNER JOIN Eligibility
ON (Claims.[SID] = Eligibility.[SID]) AND (Claims.[PHID] = Eligibility.[PHID]))
INNER JOIN (SELECT PHID, SID, MAX(TotalPaid) AS HighestDiagPaid
FROM (SELECT [PHID], [SID], DIAG_CODE1, SUM(PAID_AMT) AS TotalPaid FROM Claims GROUP BY [PHID], [SID], [DIAG_CODE1]) AS [%$###_Alias] GROUP BY PHID, SID) AS MaxDiagDollars ON ( MaxDiagDollars.[PHID]=Eligibility.[PHID] ) AND ( MaxDiagDollars.[SID] = Eligibility.[SID] )
WHERE Eligibility.DOB < DateAdd( 'y', -2, DATE())
GROUP BY
Eligibility.GROUPID, Eligibility.PHID & '-' & Eligibility.SID, [Eligibility].[DOB], Eligibility.GENDER, Eligibility.RELATIONSHIP_CODE, MaxDiagDollars.HighestDiagPaid
HAVING SUM(Claims.PAID_AMT)>10000 and MaxDiagDollars.HighestDiagPaid/SUM(Claims.PAID_AMT) <= 0.80;
This query is supposed to take the Total Amount Paid per Member and give a Total Amount PAid, and then yearly break outs.
Query 2: Member_By_Diag
SELECT
Eligibility.PHID & '-' & Eligibility.SID AS MemberID,
Claims.Diag_Code1,
ROUND(Sum(Claims.PAID_AMT)) AS TotalPaid,
ROUND(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2011',Claims.PAID_AMT,0))) AS 2011TotalPaid,
ROUND(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2012',Claims.PAID_AMT,0))) AS 2012TotalPaid,
ROUND( Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2013',Claims.PAID_AMT,0))) AS 2013TotalPaid
FROM
(Claims
INNER JOIN Eligibility
ON (Claims.[SID] = Eligibility.[SID]) AND (Claims.[PHID] = Eligibility.[PHID]))
INNER JOIN Pharmacy
ON (Eligibility.SID = Pharmacy.SID) AND (Eligibility.PHID = Pharmacy.PHID)
GROUP BY
Eligibility.PHID & '-' & Eligibility.SID, Claims.Diag_Code1
HAVING count( [Pharmacy].[NDC] ) >4 and count(IIF(Claims.REV_CODE= '450',1,0) ) > 1
ORDER BY Eligibility.PHID & '-' & Eligibility.SID;
The second query is essentially supposed to take the Codes for each member and break out their amount paids by Diagnosis code.
Query 3: combined_query
SELECT *
FROM (Member_Claims_Query AS a INNER JOIN Member_by_Diag AS b ON a.MemberID=b.MemberID) INNER JOIN Codes AS c ON c.DxCode = b.Diag_Code1;
ISSUE
My Client sent me an e-mail stating that the Total Paid in the Member_By_Diag query is sometimes higher than the Total Paid by the Member_By_Claim query. yet they are being computed the same way.
I opened up the DB and wrote a simple query to see how many records were returning where the b.Total_Paid ( Member_By_Diag.Total_Paid) is greater than the Member_Claims_Query.Total_Paid.
It returned 262/1278 records where this was the case.
SELECT * FROM Combined_Query WHERE b_TotalPaid > a_TotalPaid
This picture acurately describes what I am seeing along with my client.
As you can see. a_TotalPaid > b_TotalPaid. But if you look up at my query, they are the same? Is this a group by issue? or a join issue? Any help would be much appreciated.
There are a couple of differences between the queries that could be contributing to this. The main culprits are your INNER JOIN statements and your HAVING statement. Those could easily be excluding records that will have an effect on your TotalPaid field. Without the original dataset, there's not much I can tell you, but you may want to run those queries and play with removing and inserting the various INNER JOIN and HAVING clauses to see which one is deleting the records that are causing your totals to not be equal.
I appreciate the answers everyone... You didn't exactly answer the question, but the inner join on Pharmacy was causing the issue, I was specifically using it in relation to the HAVING clause, when I added a count(*) I noticed it was actually multiplying my results. For Ex. If a member had 7 claims, and 6 Pharmacy records it was multiplying it making it 42 records making my total paids extremely high, and they weren't relating to the CLAIMS themselves...hence the ultimate issue. Here is the solution in the Member_By_Diag Query:
SELECT Eligibility.PHID & '-' & Eligibility.SID AS MemberID, Claims.Diag_Code1, Round(Sum(Claims.PAID_AMT)) AS TotalPaid, Round(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2011',Claims.PAID_AMT,0))) AS 2011TotalPaid, Round(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2012',Claims.PAID_AMT,0))) AS 2012TotalPaid, Round(Sum(IIf(Format(Serv_Beg_Date,'yyyy')='2013',Claims.PAID_AMT,0))) AS 2013TotalPaid, Count(*) AS Expr1
FROM (Claims INNER JOIN Eligibility ON (Claims.[SID] = Eligibility.[SID]) AND (Claims.[PHID] = Eligibility.[PHID])) INNER JOIN ***(SELECT PHID, SID, COUNT(NDC) AS RXCount FROM Pharmacy GROUP BY PHID, SID ORDER BY PHID, SID) AS Pharmacy*** ON (Eligibility.SID = Pharmacy.SID) AND (Eligibility.PHID = Pharmacy.PHID)
GROUP BY Eligibility.PHID & '-' & Eligibility.SID, Claims.Diag_Code1
***HAVING Count(IIf([Claims].[REV_CODE]='450',1,0))>1***
ORDER BY Eligibility.PHID & '-' & Eligibility.SID;
This made the dollars look much more reasonable. Thanks everyone.