postgre group by having - postgresql

I have a query like that
select c.travelandsmile_id, c.name, c.surname
from customer c
where c.travelandsmile_id in
(
select s.travelandsmile_id
from spent_kilometers s
group by travelandsmile_id
having count(s.kilometers)=1
)
I want to select the records that are shown only once in the table spent_kilometers and where kilometers is greater than 30. But when I add where s.kilometers > 30, the result is wrong and more tuples appear according to first query.
How can I do that?

select travelandsmile_id, c.name, c.surname
from
customer c
inner join
spent_kilometers s using (travelandsmile_id)
where s.kilometers > 30
group by travelandsmile_id, c.name, c.surname
having count(*) = 1

If I read the question correctly you want find all customers who have one record in spent_kilometers and this record must have the constraint s.kilometers > 30.
This can be done with the following SQL.
select c.travelandsmile_id, c.name, c.surname
from customer c
where c.travelandsmile_id in
( /* find all customers that have only one record in spent_kilometers */
select c.travelandsmile_id
from spent_kilometers s
group by travelandsmile_id having count(travelandsmile_id) = 1
)
and c.travelandsmile_id in
( /* find all customers that have s.kilometers > 30 */
select c.travelandsmile_id
from spent_kilometers s
where s.kilometers > 30
);

Related

Selecting row(s) that have distinct count (one) of certain column

I have following dataset:
org system_id punch_start_tb1 punch_start_tb2
CG 100242 2022-08-16T00:08:00Z 2022-08-16T03:08:00Z
LA 250595 2022-08-16T00:00:00Z 2022-08-16T03:00:00Z
LB 300133 2022-08-15T04:00:00Z 2022-08-16T04:00:00Z
LB 300133 2022-08-16T04:00:00Z 2022-08-15T04:00:00Z
MO 400037 2022-08-15T14:00:00Z 2022-08-15T23:00:00Z
MO 400037 2022-08-15T23:00:00Z 2022-08-15T14:00:00Z
I am trying to filter out data so that it only populates the outcome when Count of "system_id" = 1.
So, the expected outcome would be only following two rows:
org system_id punch_start_tb1 punch_start_tb2
CG 100242 2022-08-16T00:08:00Z 2022-08-16T03:08:00Z
LA 250595 2022-08-16T00:00:00Z 2022-08-16T03:00:00Z
I tried with Group by and Having clause, but I did not have a success.
You can try below
SELECT * FROM
(
SELECT org,system_id,punch_start_tbl,punch_start_tb2
,ROW_NUMBER()OVER(PARTITION BY system_id ORDER BY system_id)RN
FROM <TableName>
)X
WHERE RN = 1
CTE returns org with only one record then join with main table on org column.
;WITH CTE AS (
select org
from <table_name>
group by org
Having count(1) = 1
)
select t.*
from cte
inner join <table_name> t on cte.org = t.org
You can try this (use min because we have only one row):
select MIN(org), system_id, MIN(punch_start_tb1), MIN(punch_start_tb2)
from <table_name>
group by system_id
Having count(1) = 1
or use answer #Meyssam Toluie with group by by system_id

Redshift Cross join ignoring where clause

I have the following query:
WITH MY_CTE as
(
select
....
.....
)
SELECT
MY_CTE.*
,tt.currency as most_used_currency
from MY_CTE
cross join
(select t.currency
from My_CTE t
group by t.currency
order by count(*) desc
limit 1
) tt
where MY_CTE.currency = 'EUR'
but the cross join is ignoring my where clause.
How can I enforce that it processes the where clause before working on the cross join please?
Sample data returned:
This is obviously wrong because I said do not include currency SEK, and yet it is saying its the most popular currency.
I cannot put the where clause inside of the cross join because I will be using this in tableau and need the users to be able to filter on certain criteria, e.g. currency.
The most popular currency should be EUR if the MY_CTE is filtered to show only EUR currency
WHERE condition in this case has nothing to do with cross join, it just filters rows after join is already performed. If you need to report only single currency there are simplest two options where to add currency filter (added as comments in SQL):
1) Option 1 - add filter already in CTE statement
2) Option 2 - add filter at the end (as already done) and within tt part.
WITH MY_CTE as
(
select
....
.....
/* OPTION 1*/
)
SELECT
MY_CTE.*
,tt.currency as most_used_currency
from MY_CTE
cross join
(select t.currency
from My_CTE t
/* OPTION 2 first place*/
group by t.currency
order by count(*) desc
limit 1
) tt
where MY_CTE.currency = 'EUR' /* OPTION 2a second place*/
The alias tt will return the most popular currency overall, which is SEK. If you want to filter for separate currencies, you'll need to put them in the inner query as well as the outer one. However, if that isn't an option, you'll want to return all currencies with their popularity, and filter on the most popular one you allow.
....
....
SELECT
LAST_VALUE(MY_CTE.customer_id)
OVER (partition by customer_id
ORDER BY tt.popularity
rows between unbounded preceding and unbounded following)
.... /* rest of your columns */
, LAST_VALUE(tt.currency)
OVER (partition by customer_id
ORDER BY tt.popularity
rows between unbounded preceding and unbounded following)
from MY_CTE
cross join
(select t.currency,
count(*) popularity
from My_CTE t
group by t.currency
order by count(*) desc
/* removed limit 1 */
) tt
where MY_CTE.currency = 'EUR'
AND tt.currency IN ('EUR') /* Added tt.currency filter */

what is the good way of matching group by total to whole total

I have the below problem. Seems to be a simple query, but I couldn't figure it out after spending so much time.
Table
-----
project
mission
craft
project can have many missions, for each mission, it can use multiple air crafts. I want to identify the crafts that was involved with all the missions.
For eg if there are 10 missions, mission 1 can use craft 1 and 2. If there is any craft that used for all the 10 missions (mission 1 -10), that should be output.
I wrote,
select craft,count(mission) from table group by craft
After this I don't know how can I match this value to the total missions. Any help would be highly appreciated.
If you need this for single project you need filter number of craft's missions by total number of missions:
select c.id from (
select c.id, count(mission_id) cnt
from craft c inner join mission m on (m.id = c.mission_id)
where m.project_id = <project_id>
group by c.id
) as c
where c.cnt = (select count(*) from mission where project_id = <project_id>)
If you need this for all projects:
select c.project_id, c.id from (
select m.project_id, c.id, count(mission_id) cnt
from craft c inner join mission m on (m.id = c.mission_id)
group by m.project_id, c.id
) as c inner join (
select project_id, count(*) cnt
from mission
group by project_id
) as m on (m.project_id = c.project_id)
where c.cnt = m.cnt
with mission as (
select distinct mission
from craft
), craft_missions as (
select craft, count(*) as total_missions
from
craft c
inner join
mission m on m.mission = c.mission
group by craft
)
select craft
from craft_missions
where total_missions = (select count(*) from mission)

TSQL Compare 2 select's result and return result with most recent date

Wonder if someone could give me a quick hand. I have 2 select queries (as shown below) and I want to compare the results of both and only return the result that has the most recent date.
So say I have the following 2 results from the queries:-
--------- ---------- ----------------------- --------------- ------ --
COMPANY A EMPLOYEE A 2007-10-16 17:10:21.000 E-mail 6D29D6D5 SYSTEM 1
COMPANY A EMPLOYEE A 2007-10-15 17:10:21.000 E-mail 6D29D6D5 SYSTEM 1
I only want to return the result with the latest date (so the first one). I thought about putting the results into a temporary table and then querying that but just wondering if there's a simpler, more efficient way?
SELECT * FROM (
SELECT fc.accountidname, fc.owneridname, fap.actualend, fap.activitytypecodename, fap.createdby, fap.createdbyname,
ROW_NUMBER() OVER (PARTITION BY fc.accountidname ORDER BY fap.actualend DESC) AS RN
FROM FilteredContact fc
INNER JOIN FilteredActivityPointer fap ON fc.parentcustomerid = fap.regardingobjectid
WHERE fc.statecodename = 'Active'
AND fap.ownerid = '0F995BDC'
AND fap.createdon < getdate()
) tmp WHERE RN = 1
SELECT * FROM (
SELECT fa.name, fa.owneridname, fa.new_technicalaccountmanageridname, fa.new_customerid, fa.new_riskstatusname,
fa.new_numberofopencases, fa.new_numberofurgentopencases, fap.actualend, fap.activitytypecodename, fap.createdby, fap.createdbyname,
ROW_NUMBER() OVER (PARTITION BY fa.name ORDER BY fap.actualend DESC) AS RN
FROM FilteredAccount fa
INNER JOIN FilteredActivityPointer fap ON fa.accountid = fap.regardingobjectid
WHERE fa.statecodename = 'Active'
AND fap.ownerid = '0F995BDC'
AND fap.createdon < getdate()
) tmp2 WHERE RN = 1
if the tables have the same structure (column count and column types to match), then you could just union the results of the two queries, then order by the date desc and then select the top 1.
select top 1 * from
(
-- your first query
union all
-- your second query.
) T
order by YourDateColumn1 desc
You should GROUP BY and use MAX(createdon)

Query to get row from one table, else random row from another

tblUserProfile - I have a table which holds all the Profile Info (too many fields)
tblMonthlyProfiles - Another table which has just the ProfileID in it (the idea is that this table holds 2 profileids which sometimes become monthly profiles (on selection))
Now when I need to show monthly profiles, I simply do a select from this tblMonthlyProfiles and Join with tblUserProfile to get all valid info.
If there are no rows in tblMonthlyProfile, then monthly profile section is not displayed.
Now the requirement is to ALWAYS show Monthly Profiles. If there are no rows in monthlyProfiles, it should pick up 2 random profiles from tblUserProfile. If there is only one row in monthlyProfiles, it should pick up only one random row from tblUserProfile.
What is the best way to do all this in one single query ?
I thought something like this
select top 2 * from tblUserProfile P
LEFT OUTER JOIN tblMonthlyProfiles M
on M.profileid = P.profileid
ORder by NEWID()
But this always gives me 2 random rows from tblProfile. How can I solve this ?
Try something like this:
SELECT TOP 2 Field1, Field2, Field3, FinalOrder FROM
(
select top 2 Field1, Field2, Field3, FinalOrder, '1' As FinalOrder from tblUserProfile P JOIN tblMonthlyProfiles M on M.profileid = P.profileid
UNION
select top 2 Field1, Field2, Field3, FinalOrder, '2' AS FinalOrder from tblUserProfile P LEFT OUTER JOIN tblMonthlyProfiles M on M.profileid = P.profileid ORDER BY NEWID()
)
ORDER BY FinalOrder
The idea being to pick two monthly profiles (if that many exist) and then 2 random profiles (as you correctly did) and then UNION them. You'll have between 2 and 4 records at that point. Grab the top two. FinalOrder column is an easy way to make sure that you try and get the monthly's first.
If you have control of the table structure, you might save yourself some trouble by simply adding a boolean field IsMonthlyProfile to the UserProfile table. Then it's a single table query, order by IsBoolean, NewID()
In SQL 2000+ compliant syntax you could do something like:
Select ...
From (
Select TOP 2 ...
From tblUserProfile As UP
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Order By NewId()
) As RandomProfile
Union All
Select MP....
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From (
Select TOP 1 ...
From tblUserProfile As UP
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
Order By NewId()
) As RandomProfile
Using SQL 2005+ CTE you can do:
With
TwoRandomProfiles As
(
Select TOP 2 ..., ROW_NUMBER() OVER ( ORDER BY UP.ProfileID ) As Num
From tblUserProfile As UP
Order By NewId()
)
Select MP.Col1, ...
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From TwoRandomProfiles
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Union All
Select ...
From TwoRandomProfiles
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
And Num = 1
The CTE has the advantage of only querying for the random profiles once and the use of the ROW_NUMBER() column.
Obviously, in all the UNION statements the number and type of the columns must match.