Possible to write this query without aggregate function? - sql

SELECT l.LocID, COUNT(ulr.UserID)
FROM Locations l
LEFT OUTER JOIN UserLocationRights ulr ON l.LocID = ulr.LocID
LEFT OUTER JOIN Devices d ON l.LocID = d.LocID
LEFT OUTER JOIN UserModelRights umr ON d.ModelName = umr.ModelName
AND ulr.UserID = umr.UserID
GROUP BY l.LocID, ulr.UserID, d.ModelName
ORDER BY l.LocID, ulr.UserID
I want the results to be the LocID of all of the entries in Locations and the second column to be the number of Users who have rights to this location that also have the rights to at least one of the Devices in the Location which is determined by the entries in UserModelRights.
I can only figure out how to get what I want like this:
SELECT l.LocID, IsNull(UserHasModelRightInLoc.UserCount, 0) UserCount
FROM Locations l
LEFT OUTER JOIN (
SELECT ulr.LocID, COUNT(UserModelRightsPerLocation.UserID) UserCount
FROM UserLocRights ulr
INNER JOIN (
SELECT l.LocID, umr.UserID
FROM UserModelRights umr
INNER JOIN Devices d ON umr.ModelName = d.ModelName
INNER JOIN Locations l ON d.LocID = l.LocID
GROUP BY umr.ModelName, umr.UserID, l.LocID
) UserModelRightsPerLocation ON ulr.LocID = UserModelRightsPerLocation.LocID
AND ulr.UserID = UserModelRightsPerLocation.UserID
GROUP BY ulr.LocID
) UserHasModelRightInLoc ON l.LocID = UserHasModelRightInLoc.LocID
ORDER BY l.LocID
I don't know if this is possible, but I assume that there could be a way to use GROUP BY to get what I want using my first, much smaller query. The issue is that I think I need to do multiple GROUP BY in a specific order and I don't know if that's possible or makes sense in SQL.
Is there a way to get the result I want without using an aggregate function? If not, maybe narrow it down to a single one?

I find this a bit hard to follow without sample data. But based on your description, this might do what you want:
SELECT l.LocID, COUNT(DISTINCT ulr.UserID)
FROM Locations l LEFT JOIN
UserLocationRights ulr
ON l.LocID = ulr.LocID LEFT JOIN
Devices d
ON l.LocID = d.LocID LEFT JOIN
UserModelRights umr
ON d.ModelName = umr.ModelName AND
ulr.UserID = umr.UserID
GROUP BY l.LocID
ORDER BY l.LocID;

Related

Can't get the SUM function to work with my query

I'm trying to SUM some warehouse stock values, grouped by zones, but can't get it to work.
The following query returns all materials on stock. Same material is placed on more locations, in more zones. So material AAA can be located on two locations in zone 111, and three locations in zone 222
SELECT Zones.Label,
Materials.Material,
Shelves.Label as ShelfLabel,
Items.StockQty
FROM Zones INNER JOIN
(dbo.LangTexts(null) AS Texts INNER JOIN (WarehouseStatus INNER JOIN
(Shelves INNER JOIN (Positions INNER JOIN
(Materials INNER JOIN Items
ON Materials.Material = Items.Material)
ON Positions.ID = Items.Owner)
ON Shelves.ID = Positions.Owner)
ON WarehouseStatus.Status = Items.Status)
ON Texts.TextID = WarehouseStatus.Text)
ON Zones.ID = Shelves.Owner
WHERE Zones.Label IS NOT NULL
ORDER BY Materials.Material, Zones.Label
Output:
output screenshot
As I have tried to markup in the screenshot, I need the total StockQty grouped by zone (label column)
I'm trying to achieve that by modifying the query like this.
SELECT Zones.Label,
Materials.Material,
Shelves.Label as ShelfLabel,
SUM(Items.StockQty)
FROM Zones INNER JOIN
(dbo.LangTexts(null) AS Texts INNER JOIN (WarehouseStatus INNER JOIN
(Shelves INNER JOIN (Positions INNER JOIN
(Materials INNER JOIN Items
ON Materials.Material = Items.Material)
ON Positions.ID = Items.Owner)
ON Shelves.ID = Positions.Owner)
ON WarehouseStatus.Status = Items.Status)
ON Texts.TextID = WarehouseStatus.Text)
ON Zones.ID = Shelves.Owner
WHERE Zones.Label IS NOT NULL
GROUP BY Zones.Label
ORDER BY Materials.Material, Zones.Label
But it keeps returning the following error.
Msg 8120, Level 16, State 1, Line 6
Column 'Materials.Material' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I have been dealing with such error before, but don't understand exactly what I'm doing wrong. Sometimes I succeed by changing the query back and forth, but this time I really stuck.
Hope that someone is possible to guide me.
Thank's in advance.
As the column Shelves.Label actually not bring any value in this matter, I ended up with the following query based on #Md.SumanKabir's answer.
SELECT Zones.Label,
Materials.Material,
SUM(Items.StockQty)
FROM Zones INNER JOIN
(dbo.LangTexts(null) AS Texts INNER JOIN (WarehouseStatus INNER JOIN
(Shelves INNER JOIN (Positions INNER JOIN
(Materials INNER JOIN Items
ON Materials.Material = Items.Material)
ON Positions.ID = Items.Owner)
ON Shelves.ID = Positions.Owner)
ON WarehouseStatus.Status = Items.Status)
ON Texts.TextID = WarehouseStatus.Text)
ON Zones.ID = Shelves.Owner
WHERE Zones.Label IS NOT NULL
GROUP BY Zones.Label, Materials.Material
ORDER BY Materials.Material, Zones.Label
The correct query would be :
SELECT Zones.Label,
Materials.Material,
Shelves.Label as ShelfLabel,
SUM(Items.StockQty)
FROM Zones INNER JOIN
(dbo.LangTexts(null) AS Texts INNER JOIN (WarehouseStatus INNER JOIN
(Shelves INNER JOIN (Positions INNER JOIN
(Materials INNER JOIN Items
ON Materials.Material = Items.Material)
ON Positions.ID = Items.Owner)
ON Shelves.ID = Positions.Owner)
ON WarehouseStatus.Status = Items.Status)
ON Texts.TextID = WarehouseStatus.Text)
ON Zones.ID = Shelves.Owner
WHERE Zones.Label IS NOT NULL
GROUP BY Zones.Label, Materials.Material, Shelves.Label
ORDER BY Materials.Material, Zones.Label
You need to use the columns in GROUP BY which are not used in any aggregate functions like SUM
You can get more information on how to use GROUP BY Here
The problem here is that your grouping does not match your select. You select 3 columns Zones.Label, Materials.Material and Shelves.Label, but you group only by Zones.Label. Therefore, it does not know what to do with the 2 other columns.
Either you have to aggregate them like you did with the sum or you have to add them to the grouping.

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

Limitting results in association

I want to limit the results in a lateral join, so that it only returns the N most recent matches.
This is my query, but the limit inside the join does not seem to work, as it returns all visitors
select am.id, am.title, ame.event, array_agg(row_to_json(visitors))
from auto_messages am
left join apps a on am.app_id = a.id
left join app_users au on a.id = au.app_id
left join auto_message_events ame on ame.auto_message_id = am.id
left join lateral (
select
id,
name,
avatar,
ame.inserted_at
from visitors v
where v.id = ame.visitor_id
order by ame.inserted_at desc
limit 1
) as visitors on visitors.id = ame.visitor_id
where am.id = '100'
group by am.id, ame.event
I am pretty sure the problem is with ame. That is where the rows are generated. The join to visitors is only picking up additional information.
So, this might solve your problem:
select am.id, am.title, visitors.event, array_agg(row_to_json(visitors))
from auto_messages am left join
apps a
on am.app_id = a.id left join
app_users au
on a.id = au.app_id left join lateral
(select v.id, v.name, v.avatar,
ame.event, ame.inserted_at, ame.auto_message_id
from auto_message_events ame join
visitors v
on v.id = ame.visitor_id
order by ame.inserted_at desc
limit 1
) visitors
on visitors.auto_message_id = am.id
where am.id = '100'
group by am.id, visitors.event;
You also might want to change your select clause, if you only want a subset of columns.

Return rows where a customer bought things on same day

Can someone help me with the rest of my Query.
This query gives me Customer, AdressNr, Date, Employee, Article, ActivityNr
from all the sales in my Company.
SELECT ad.Name + ' ' + ad.Vorname AS Customer,
pa.Kunde AS CustomerNr,
CONVERT(VARCHAR(10),p.datum,126) AS Date,
(SELECT a.name + ' ' + a.Vorname AS Name FROM PRO_Mitarbeiter m LEFT JOIN ADR_Adressen a ON a.AdressNrADR=m.AdressNrADR WHERE m.MitNrPRO = l.MitNrPRO) as Employee,
p.Artikel_1 AS Article,
l.AufgabenNrCRM AS OrderNr
FROM ZUS_Therapie_Positionen p
INNER JOIN CRM_AufgabenLink l ON l.AufgabenNrCRM = p.Id_Aktivitaet
INNER JOIN CRM_Aufgaben ab ON ab.AufgabenNrCRM = p.Id_Aktivitaet
INNER JOIN PRO_Auftraege pa ON pa.AuftragNrPRO = ab.AuftragNrPRO
INNER JOIN ADR_Adressen ad ON ad.AdressNrADR = pa.Kunde
INNER JOIN ADR_GruppenLink gl ON gl.AdressNrADR = ad.AdressNrADR
INNER JOIN ADR_Gruppen g ON g.GruppeADR = gl.GruppeADR
WHERE l.MitNrPRO != 0
GROUP BY l.AufgabenNrCRM,ad.Name,ad.Vorname,pa.Kunde,p.datum,p.Artikel_1,l.MitNrPRO
ORDER BY pa.Kunde,p.datum,l.AufgabenNrCRM
My goal is to filter this so i get only rows back where the customer has bought more then 1 Thing on the same day. It doesn't matter if a customer bought the same Article twice on the same day. I want too see this also.
It's to complicated to write some SQL Fiddle for you but in this Picture you can see what my goal is. I want to take away all rows with an X on the left side and thoose with a Circle i want to Keep.
As I don't speak German, I won't target this specifically to your SQL. But see the following quasi-code for a similar example that you should be able to apply to your own script.
SELECT C.CustomerName, O.OrderDate, O.OrderNumber
FROM CUSTOMER C
JOIN ORDERS O ON O.Customer_ID = C.Customer_ID
JOIN
(SELECT Customer_ID, OrderDate
FROM ORDERS
GROUP BY Customer_ID, OrderDate
HAVING COUNT(*) > 1) SRC
ON SRC.Customer_ID = O.Customer_ID AND SRC.OrderDate = O.OrderDate
In the script above, the last query (a subquery) would only return results where a customer had more than one order in a given day. By joining that to your main query, you would effectively produce the result asked in the OP.
Edit 1:
Regarding your comment below, I really recommend just going over your datamodel, trying to understand what's happening here, and fixing it on your own. But there is an easy - albeit hardly optimal solution to this by just using your own script above. Note, while this is not disastrous performance-wise, it's obviously not the cleanest, most effective method either. But it should work:
;WITH CTE AS (SELECT ad.Name + ' ' + ad.Vorname AS Customer,
pa.Kunde AS CustomerNr,
CONVERT(VARCHAR(10),p.datum,126) AS [Date],
(SELECT a.name + ' ' + a.Vorname AS Name FROM PRO_Mitarbeiter m LEFT JOIN ADR_Adressen a ON a.AdressNrADR=m.AdressNrADR WHERE m.MitNrPRO = l.MitNrPRO) as Employee,
p.Artikel_1 AS Article,
l.AufgabenNrCRM AS OrderNr
FROM ZUS_Therapie_Positionen p
INNER JOIN CRM_AufgabenLink l ON l.AufgabenNrCRM = p.Id_Aktivitaet
INNER JOIN CRM_Aufgaben ab ON ab.AufgabenNrCRM = p.Id_Aktivitaet
INNER JOIN PRO_Auftraege pa ON pa.AuftragNrPRO = ab.AuftragNrPRO
INNER JOIN ADR_Adressen ad ON ad.AdressNrADR = pa.Kunde
INNER JOIN ADR_GruppenLink gl ON gl.AdressNrADR = ad.AdressNrADR
INNER JOIN ADR_Gruppen g ON g.GruppeADR = gl.GruppeADR
WHERE l.MitNrPRO != 0
GROUP BY l.AufgabenNrCRM,ad.Name,ad.Vorname,pa.Kunde,p.datum,p.Artikel_1,l.MitNrPRO
ORDER BY pa.Kunde,p.datum,l.AufgabenNrCRM)
SELECT C.*
FROM CTE C
JOIN (Select CustomerNr, [Date]
FROM CTE B
GROUP BY CustomerNr, [Date]
HAVING COUNT(*) > 1) SRC
ON SRC.CustomerNr = C.CustomerNr AND SRC.[Date] = C.[Date]
This should work directly. But as I said, this is an ugly workaround where we're basically all but fetching the whole set twice, as opposed to just limiting the sub query to just the bare minimum of necessary tables. Your choice. :)
Tried that also and it didnt work. I also made a new query trying to Keep it so simple as possible and it doesnt work either. It still give me Single values back..
SELECT p.Datum,a.AufgabenNrCRM,auf.Kunde FROM CRM_Aufgaben a
LEFT JOIN ZUS_Therapie_Positionen p ON p.Id_Aktivitaet = a.AufgabenNrCRM
LEFT JOIN PRO_Auftraege auf ON auf.AuftragNrPRO = a.AuftragNrPRO
LEFT JOIN
(SELECT pa.Datum,au.Kunde FROM CRM_Aufgaben aa
LEFT JOIN ZUS_Therapie_Positionen pa ON pa.Id_Aktivitaet = aa.AufgabenNrCRM
LEFT JOIN PRO_Auftraege au ON au.AuftragNrPRO = aa.AuftragNrPRO
GROUP BY pa.Datum,au.Kunde
HAVING COUNT(*) > 1) SRC
ON SRC.Kunde = auf.Kunde
WHERE p.datum IS NOT NULL
GROUP BY p.Datum,a.AufgabenNrCRM,auf.Kunde
ORDER BY auf.Kunde,p.Datum

SQL Error: ORA-01489: result of string concatenation is too long

I am working on the query below:
select ip.intake_id,
ip.estimated_years,
ip.gender_code,
LISTAGG(ip.race_code, ',') WITHIN GROUP (ORDER BY ip.race_code) as race_code,
eth.ethnicity_code,
i.living_arrangements,
p.dep_actv_military_flag,
LISTAGG(ale.allegation_super_type_code) WITHIN GROUP (ORDER BY ale.allegation_super_type_code) as maltreatment_type_code,
LISTAGG(ale.initial_report_disp_code) WITHIN GROUP (ORDER BY ale.initial_report_disp_code) as maltreatment_dispo_lvl,
ip.deceased_flag,
LISTAGG(ch.characteristic_code, ',') WITHIN GROUP (ORDER BY ch.characteristic_code) as chara_codes,
LISTAGG(ich.intake_characteristic_code, ',') WITHIN GROUP (ORDER BY ich.intake_characteristic_code) as intake_chara_codes,
pe.removed_date,cm.petition_submitted_flag,cm.created_date,atr.person_id
from intake i inner join intake_participant ip on i.intake_id = ip.intake_id
left outer join reporter r ON i.intake_id=r.intake_id
left outer join ethnicity eth on eth.person_id = ip.person_id
left outer join person p on p.person_id = ip.person_id
left outer join allegation ale on ale.intake_id = i.intake_id
left outer join characteristic ch on ch.person_id = ip.person_id
left outer join intake_characteristic ich on ich.intake_id = i.intake_id
left outer join placement_episode pe on pe.child_id = ip.person_id
left outer join complaint cm on cm.petitioner_id = ip.person_id
left outer join attorney atr on atr.person_id = ip.person_id
left outer join intake_participant_role apr on apr.intake_participant_id = ip.intake_participant_id
group by ip.intake_id,ip.estimated_years,ip.gender_code,eth.ethnicity_code,i.living_arrangements,p.dep_actv_military_flag,
ip.deceased_flag,pe.removed_date,cm.petition_submitted_flag,cm.created_date,atr.person_id
when I am running this query I am getting following error message:
Error report:
SQL Error: ORA-01489: result of string concatenation is too long
01489. 00000 - "result of string concatenation is too long"
*Cause: String concatenation result is more than the maximum size.
*Action: Make sure that the result is less than the maximum size.
But when I remove the line :
left outer join reporter r ON i.intake_id=r.intake_id
from my query then it executes without any error message. The working query is given below:
select ip.intake_id,
ip.estimated_years,
ip.gender_code,
LISTAGG(ip.race_code, ',') WITHIN GROUP (ORDER BY ip.race_code) as race_code,
eth.ethnicity_code,
i.living_arrangements,
p.dep_actv_military_flag,
LISTAGG(ale.allegation_super_type_code) WITHIN GROUP (ORDER BY ale.allegation_super_type_code) as maltreatment_type_code,
LISTAGG(ale.initial_report_disp_code) WITHIN GROUP (ORDER BY ale.initial_report_disp_code) as maltreatment_dispo_lvl,
ip.deceased_flag,
LISTAGG(ch.characteristic_code, ',') WITHIN GROUP (ORDER BY ch.characteristic_code) as chara_codes,
LISTAGG(ich.intake_characteristic_code, ',') WITHIN GROUP (ORDER BY ich.intake_characteristic_code) as intake_chara_codes,
pe.removed_date,cm.petition_submitted_flag,cm.created_date,atr.person_id
from intake i inner join intake_participant ip on i.intake_id = ip.intake_id
left outer join ethnicity eth on eth.person_id = ip.person_id
left outer join person p on p.person_id = ip.person_id
left outer join allegation ale on ale.intake_id = i.intake_id
left outer join characteristic ch on ch.person_id = ip.person_id
left outer join intake_characteristic ich on ich.intake_id = i.intake_id
left outer join placement_episode pe on pe.child_id = ip.person_id
left outer join complaint cm on cm.petitioner_id = ip.person_id
left outer join attorney atr on atr.person_id = ip.person_id
left outer join intake_participant_role apr on ipr.intake_participant_id = ip.intake_participant_id
group by ip.intake_id,ip.estimated_years,ip.gender_code,eth.ethnicity_code,i.living_arrangements,p.dep_actv_military_flag,
ip.deceased_flag,pe.removed_date,cm.petition_submitted_flag,cm.created_date,atr.person_id
I am not sure why this error occured. Can someone help me to figure out the problem? I got same questions from these links link1 and link2, but I didn't get the solution to my question from these links.
The join to table "reporter" is probably increasing the record count (this could only be the case if column "intake_id" is not a unique key of "reporter"). By increasing the record count, you are generating more strings that LISTAGG must concatenate together within each group. If the total length of concatenated strings exceeds 4000 bytes, LISTAGG will fail with the error you see.
Let's take a different approach: just like when you're aggregating sums and joining to other tables, sometimes you have to materialize the results before you additional one-to-many join so your aggregation isn't artificially inflated due to multiple records in joining tables. This approach is assuming you don't need the repetition that is occurring due to the additional table joins however.
I think by creating the inline view the results of the race_Code will fit within size limits. This could be done using a CTE as well. Simply put, provided you don't need the record duplication, you may need to materialize the listAgg results individually first then join them back in. If you have other problems with the other listAggs, you may want to create multiple CTE's and then join them all back together in the end. This approach simply uses a inline view.
select B.intake_id,
B.estimated_years,
B.gender_code,
B.race_code,
eth.ethnicity_code,
B.living_arrangements,
p.dep_actv_military_flag,
LISTAGG(ale.allegation_super_type_code) WITHIN GROUP (ORDER BY ale.allegation_super_type_code) as maltreatment_type_code,
LISTAGG(ale.initial_report_disp_code) WITHIN GROUP (ORDER BY ale.initial_report_disp_code) as maltreatment_dispo_lvl,
B.deceased_flag,
LISTAGG(ch.characteristic_code, ',') WITHIN GROUP (ORDER BY ch.characteristic_code) as chara_codes,
LISTAGG(ich.intake_characteristic_code, ',') WITHIN GROUP (ORDER BY ich.intake_characteristic_code) as intake_chara_codes,
pe.removed_date,cm.petition_submitted_flag,cm.created_date,atr.person_id
from (SELECT ip.intake_id,
ip.estimated_years,
ip.gender_code,
ip.deceased_flag,
ip.person_id,
ip.intake_participant_id,
LISTAGG(ip.race_code, ',') WITHIN GROUP (ORDER BY ip.race_code) as race_code, i.living_arrangements
FROM intake i
INNER JOIN intake_participant ip on i.intake_id = ip.intake_id
GROUP BY ip.intake_id, ip.estimated_years, ip.gender_code,
i.living_arrangements, ip.deceased_flag, ip.person_id,
ip.intake_participant_id) B
left outer join ethnicity eth on eth.person_id = B.person_id
left outer join person p on p.person_id = B.person_id
left outer join allegation ale on ale.intake_id = B.intake_id
left outer join characteristic ch on ch.person_id = B.person_id
left outer join intake_characteristic ich on ich.intake_id = i.intake_id
left outer join placement_episode pe on pe.child_id = B.person_id
left outer join complaint cm on cm.petitioner_id = B.person_id
left outer join attorney atr on atr.person_id = B.person_id
left outer join intake_participant_role apr on ipr.intake_participant_id = B.intake_participant_id
GROUP BY ip.intake_id,ip.estimated_years,ip.gender_code, eth.ethnicity_code,i.living_arrangements,p.dep_actv_military_flag, ip.deceased_flag,pe.removed_date,cm.petition_submitted_flag, cm.created_date,atr.person_id