postgreSQL select additional columns that aren't used in aggregate function - database

I'm trying to write a query in PostgreSQL and I'm getting a little frustrated because it works in other database engines. I need to select the top 5 users from a given joins table like this:
SELECT users.*,
COUNT(deals.id) AS num_deals
FROM users, deals
WHERE deals.users_id = users.id
GROUP BY users.id
ORDER BY num_deals LIMIT 5;
I need the top 5 users. This code works in sqlite, mysql, etc, yet PostgreSQL refuses to select additional fields that aren't used in aggregate functions. I'm getting the following error:
PGError: ERROR: column "users.id" must appear in the GROUP BY clause or be used in an aggregate function
How can I do this in PostgreSQL??

You could try:
SELECT users.*, a.num_deals FROM users, (
SELECT deal.id as dealid, COUNT(deals.id) AS num_deals
FROM deals
GROUP BY deal.id
) a where users.id = a.dealid
ORDER BY a.num_deals DESC
LIMIT 5

Assuming that users.id IS a PK, then you can either
wait for 9.1
group by all fields
use an aggregate (i.e. max() ) on all fields

One other solution that works is to use all attributes implicitly in GROUP BY
Thus following will be final query
SELECT users.*,
COUNT(deals.id) AS num_deals
FROM users, deals
WHERE deals.users_id = users.id
GROUP BY users.id, users.name, users.attrib1, ..., users.attribN
ORDER BY num_deals LIMIT 5;
If you are using framework like rails then you can implement this easily with Model.column_names function.

Just in case of somebody wants ANSI-92 standard solution and doesn't like 'Oracle' way to join tables...
SELECT users.*, num_deals
FROM users
JOIN
(SELECT deals.users_id as users_id, count(deals.users_id) as num_deals
FROM deals
GROUP BY deals.id) grouped_user_deals
ON grouped_user_deals.users_id = users.id
ORDER BY num_deals DESC
LIMIT 5;

Related

How to JOIN 2 Tables in SalesForce (SOQL)

I'm new to SalesForce and SOQL and was surprised that simple JOIN can become a real problem for me.
There are 2 Tables (Account and Intake__c) that I want to INNER JOIN. The only data I need from Account Table is Client Name (Name).
I was able to run 2 queries separately with no errors.
Account:
SELECT
a.Id,
a.Name
FROM Account AS a
Intake__c:
SELECT
i.Client_Id__c,
i.Intake_Id__c,
i.Intake_Name__c,
i.Intake_Status__c
FROM Intake__c AS i
However, when I try to join them, I get the error:
MALFORMED_QUERY: ERROR at Row:1:Column:151 unexpected token: 'JOIN'
SELECT
i.Client_Id__c,
a.Name,
i.Intake_Id__c,
i.Intake_Name__c,
i.Intake_Status__c
FROM Intake__c AS i
JOIN Account AS a
ON (i.Client_Id__c = a.Id)
SOQL syntax for joins is special, looks bit object-oriented. https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_relationships_and_custom_objects.htm
You probably need
SELECT Client_Id__r.Name, Intake_Id__c, Intake_Name__c, Intake_Status__c
FROM Intake__c
The "__r" bit is called relationship name and acts bit like a table alias in JOIN. You can travel via "dot" up to 5 times (see, "object-oriented").
And if you'd need a top-down approach (left outer join starting from account) it'd probably be something like
SELECT Id, Name,
(SELECT Intake_Status__c FROM Intakes__r)
FROM Account

Return multiple rows with subquery in SQL select clause

Is there a way to return multiple rows in a SQL select clause, when your subquery does not have a join/key field? My query currently looks like this. I'm wanting to return list of users and a list of contracts when there is no key between the users and contracts.
There is a handful of users, but a whole lot of contracts and I'm wanting to generate a list of each contractID next to each userID.
select
userid,
(select contractid from contracts) as contractid
from users
New here, but the suggestion for a cross join did what I wanted. thanks!
You can generate all possible combinations of users with constracts by performing a CROSS JOIN. For example:
select
u.*,
c.*
from users u
cross join contracts c
You can, then, filter the result by appending WHERE <condition> as needed.

Using SQL to combine detailed and aggregated results

I am developing a report against a SQL Server database. Using the query presented here...
SELECT
f.FacilityID as 'FID',
COUNT (DISTINCT f.PhoneTypeID) as 'Ptypes',
COUNT (DISTINCT f.PhoneID) as 'Pnumbers'
from dbo.FacilityPhones as f
inner join
dbo.Phones as ph
f.PhoneID = ph.PhoneID
group by f.FacilityID
having COUNT(DISTINCT f.PhoneTypeID)<>COUNT(DISTINCT f.PhoneId);
...I have identified 107 records where the number of phone numbers present for a Facility differs from the number of phone number types (e.g., there are two distinct phone numbers, both listed as primary).
I would like to be able to produce a detailed report that would list phone numbers and phone types for each facility, but ONLY when the distinct counts differ.
Is there a way to do this with a single query? Or would I need to save the summaries to a temp table, then join back to that temp table to get the details?
Not sure what fields exist in dbo.Phone; but assume the number comes from there... Likely need to join to the type table to get it's description as well...
This uses a common table expression to get your base list of items an then a correlated subquery to ensure only those facilities in your cte are displayed.
WITH CTE AS (
SELECT f.FacilityID as 'FID'
, COUNT (DISTINCT f.PhoneTypeID) as 'Ptypes'
, COUNT (DISTINCT f.PhoneID) as 'Pnumbers'
FROM dbo.FacilityPhones as f
GROUP BY f.FacilityID
HAVING COUNT(DISTINCT f.PhoneTypeID)<>COUNT(DISTINCT f.PhoneId))
SELECT *
FROM dbo.FaclityPhones FP
INNER JOIN dbo.Phones as ph
ON FP.PhoneID = ph.PhoneID
WHERE EXISTS (SELECT 1
FROM CTE
WHERE FID = FP.FacilityID)
The where clause here just says only show those FacilityID's and associated records if the FacilityID exists in your original query (CTE) (107) If we needed data from the CTE we'd join to it; but as it's simply restricting data placing it in the where clause and using an exists will likely be more efficient.

Doing a join with SOQL

Here is my SOQL problem.
Query 1:
Select
c.Date_Joined__c,
c.Email,
c.FirstName,
c.LastName,
c.regcode__c
from Contact c WHERE c.regcode__c ='XXXXXXXXX'
Query 2:
Select
p.Account__c,
p.Date__c,
p.Points__c,
p.Description__c,
p.Code__c
from Points__c p where p.Account__c ='YYYYYYYYYYYY' and (p.Points__c > 0)
Order by p.Date__c DESC
The relationship between the two queries is that c.regcode__c will have the same value as p.Code__c.
I want to combine Query1 and Query2, so c.regcode__c = p.Code__c
I'm stuck, I can't seem to get the syntax right for SOQL.
Is it even possible to do joins in the API?
You can't really create a join per se but you can do some filtering using a syntax similar to this:
SELECT Id FROM Contact WHERE c.RegCode__c IN (SELECT p.Code__c FROM Account)
As long as the subquery in the WHERE clause is only returning a single value and codes is a filterable field this should work. Also, this doesn't work if you were trying to filter by the same object (i.e. Account to Account). You can add more criteria to the account side to match your example queries.
Again, this isn't a true join so you can't put the account fields from your subquery. But you can at least filter down your contacts.

Getting Columns Related to GROUP BY Column

I'd like to do something like the following.
SELECT aspnet_Users.UserName, aspnet_Membership.Email, count(*) as Activities
FROM aspnet_Users
INNER JOIN Activities ON aspnet_Users.UserId = Activities.ActUserID
INNER JOIN aspnet_Membership ON aspnet_Users.UserId = aspnet_Membership.UserId
WHERE Activities.ActDateTime >= GETDATE()
GROUP BY aspnet_Users.UserName
ORDER BY Activities DESC
But this gives me an error.
Column 'aspnet_Membership.Email' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I understand the error somewhat. I'm trying to select a column that is not part of the grouping.
However, there will always be a one-to-one relationship between aspnet_Membership.Email and aspnet_Users.UserId. So how would I implement this?
Change:
GROUP BY aspnet_Users.UserId
To:
GROUP BY aspnet_Users.UserName, aspnet_Membership.Email
Not sure why you think you need to mention the UserId column in the grouping if you don't want to return it, or why you think you shouldn't group by the columns you do want to return.
to select a column it must either be in a group by clause or aggregated ,you could consider grouping by (aspnet_Users.UserName, aspnet_Membership.Email,aspnet_Users.UserId ) .
my guess is it would work

Resources