Count and max aggregate function in same table in one query - sql

I have to do count and max aggregate function in same query. For example I have history table contains date column. I need to retrieve the latest date as well as count () with some criteria. Criteria is applicable for only count() . I am able to retrieve the latest date using max and rank function.But could not merge both. Could you please assist?
Update:
Scenario : Customer buys/sells Shares.
Input: Table Share_history and Table Customer and Table Share and Table Share_Status
Customer :
Cust_id |Cust_name
1 |A
2 |B
Share :
Share_id|Share_Name|Owner|
10 |ABC |XYZ |
20 |BCD |MNC |
Share_Status :
Share_Status_Id|Share_Status_Name
1 |Buy
2 |Sell
Share_history :
Share_history _id|Share_id|Trans_date|Share_status_Id|Cust_id
100 |10 |12/12/14 | 1 |1
101 |10 |24/12/14 | 2 |1
102 |10 |14/01/15 | 1 |1
103 |10 |28/02/15 | 2 |1
103 |10 |16/03/15 | 1 |1
Output: latest Trans_date and count(no of times specific share was bought(1)) and Cust_id=1.
Query:
select share1.Share_id,SHAREHIST.Latest_Date,SHAREHIST.buycount
from Share share1 left outer join
(select share_id,max(Trans_date) keep(dense_rank last order by share_id) as Latest_Date,
(select count(*) as buycount from Share_history where Share_status_id=1 and Share_id=share1.Share_id)
from Share_history
group by Share_id
) SHAREHIST
on SHAREHIST.share_id=share1.share_id
EXPECTED :
Share_id|Latest_Date|buycount
10 |16/03/15 | 3

Try using this:
SELECT
Share_id
,Trans_Date
,COUNT(Share_id) buycount
FROM
(
SELECT
*
FROM Share_history SH
WHERE Trans_Date = (SELECT MAX(Trans_Date) FROM Share_history)
) SH
GROUP BY Share_id, Trans_Date
Rest of the joins I think you can add.

I think you just want aggregation:
select sh.share_id, max(trans_date) as trans_date, count(*) as buy_count,
from share_history sh
where cust_id = 1
group by sh.share_id;

Related

How to merge dates between 2 locations using SQL?

TIA for any assistance towards this problem, I am rather new to SQL/SSMS.
I would like to understand how I can create date rows for 1 location based on another. For example, I have the following table:
ClientFK | LocationFK | Month | Sales
---------|------------|-----------|-------
15 |1 |2019-04-01 | $100
15 |2 |2019-04-01 | $50
15 |2 |2019-03-01 | $30
15 |2 |2019-02-01 | $20
How can I create rows in location 1 in which location 2 had sales? The output would look like this:
ClientFK | LocationFK | Month | Sales
---------|------------|-----------|-------
15 |1 |2019-04-01 | $100
15 |1 |2019-03-01 | $0
15 |1 |2019-02-01 | $0
15 |2 |2019-04-01 | $50
15 |2 |2019-03-01 | $30
15 |2 |2019-02-01 | $20
My goal is to make this dynamic, so I'm not trying to work with this specific client/location, it is just an example. Ideally this should work for any client/location combo.
Again, I'm rather new to this, and wasn't sure how to best ask this question. Any advice on how to clarify what I'm asking would be much appreciated as well. Thanks!
This will give you all months for all locations and all clients.
with AvailableDates as(
select distinct [Month] from MonthlySales
),
Locations as(
select distinct LocationFk from MonthlySales
),
Clients as(
select distinct ClientFk from MonthlySales
)
select c.ClientFk, l.LocationFk, ad.Month, IsNull(ms.Sales,0) as Sales
from Locations l
left join AvailableDates ad on 1=1
left join clients c on 1=1
left join MonthlySales ms on
l.LocationFk=ms.LocationFk
and c.ClientFk = ms.ClientFk
and ad.Month = ms.Month
order by locationFK, Month desc
Here is a way to go about with this. First find out all of the unique months for Location2.
Then carterisan prod with the Client1 data.
After that you have all of the records for Client1 and Client2 in the carteisan prod block, which would left join with the data and set up the sales=0 for those missing months data.
with loc2_data
as (select distinct month
from tbl
where locationfk=2
and ClientFK=15
)
,cartesian_prod
(
select a.ClientFK,a.LocationFK,b.Month
from tbl a
join loc2_data b
on 1=1
where a.locationfk=1
and a.ClientFK=15
)
select a.ClientFK,a.LocationFK,a.Month,isnull(v.Sales,0) as sales
from cartesian_prod a
left join tbl v
on a.ClientFK=v.ClientFK
and a.LocationFK=v.LocationFK
and a.Month=v.Month

Iterating over groups in table

I have the following data:
cte1
===========================
m_ids |p_id |level
---------|-----------|-----
{123} |98 |1
{123} |111 |2
{432,222}|215 |1
{432,222}|215 |1
{432,222}|240 |2
{432,222}|240 |2
{432,222}|437 |3
{432,222}|275 |3
I have to perform the following operation:
Extract p_id by the following algorithm
For every row with same m_ids
In each group:
2.I. Group records by p_id
2.II. Order desc records by level
2.III. Select p_id with exact count as the m_ids length and with the biggest level
So far I fail to write this algorithm completely, but I wrote (probably wrong where I'm getting array_length) this for the last part of it:
SELECT id
FROM grouped_cte1
GROUP BY id,
level
HAVING Count(*) = array_length(grouped_cte1.m_ids, 1)
ORDER BY level DESC
LIMIT 1
where grouped_cte1 for m_ids={123} is
m_ids |p_id |level
---------|-----------|-----
{123} |98 |1
{123} |111 |2
and for m_ids={432,222} is
m_ids |p_id |level
---------|-----------|-----
{432,222}|215 |1
{432,222}|215 |1
{432,222}|240 |2
{432,222}|240 |2
{432,222}|437 |3
{432,222}|275 |3
etc.
2) Combine query from p.1 with the following. The following extracts p_id with level=1 for each m_ids:
select m_ids, p_id from cte1 where level=1 --also selecting m_ids for joining later`
which results in the following:
m_ids |p_id
---------|----
{123} |98
{432,222}|215
Desirable result:
m_ids |result_1 |result_2
---------|-----------|--------
{123} |111 |98
{432,222}|240 |215
So could anyone please help me solve the first part of algorithm and (optionally) combine it in a single query with the second part?
EDIT: So far I fail at:
1. Breaking the presented table into subtables by m_ids while iterating over it.
2. Performing computation of array_length(grouped_cte1.m_ids, 1) for corresponding rows in query.
For the first part of the query you're on the right track, but you need to change the grouping logic and then join again to the table to filter it out by highest level per m_ids for which you could use DISTINCT ON clause combined with proper sorting:
select
distinct on (t.m_ids)
t.m_ids, t.p_id, t.level
from cte1 t
join (
select
m_ids,
p_id
from cte1
group by m_ids, p_id
having count(*) = array_length(m_ids, 1)
) as g using (m_ids, p_id)
order by t.m_ids, t.level DESC;
This would give you:
m_ids | p_id | level
-----------+------+-------
{123} | 111 | 2
{432,222} | 240 | 2
And then when combined with second query (using FULL JOIN for displaying purposes, when the first query is missing such conditions) which I modified by adding distinct since there can be (and in fact is) more than one record for m_ids, p_id pair with first level it would look like:
select
coalesce(r1.m_ids, r2.m_ids) as m_ids,
r1.p_id AS result_1,
r2.p_id AS result_2
from (
select
distinct on (t.m_ids)
t.m_ids, t.p_id, t.level
from cte1 t
join (
select
m_ids,
p_id
from cte1
group by m_ids, p_id
having count(*) = array_length(m_ids, 1)
) as g using (m_ids, p_id)
order by t.m_ids, t.level DESC
) r1
full join (
select distinct m_ids, p_id
from cte1
where level = 1
) r2 on r1.m_ids = r2.m_ids
giving you result:
m_ids | result_1 | result_2
-----------+----------+----------
{123} | 111 | 98
{432,222} | 240 | 215
that looks different from what you've expected but from my understanding of the logic it is the correct one. If I misunderstood anything, please let me know.
Just for the sake of logic explanation, one point:
Why m_ids with {123} returns 111 for result_1?
for group of m_ids = {123} we have two distinct p_id values
both 98 and 111 account for the condition of equality count with the m_ids length
p_id = 111 has a higher level, thus is chosen for the result_1

Select rows that are duplicates on two columns

I have data in a table. There are 3 columns (ID, Interval, ContactInfo). This table lists all phone contacts. I'm attempting to get a count of phone numbers that called twice on the same day and have no idea how to go about this. I can get duplicate entries for the same number but it does not match on date. The code I have so far is below.
SELECT ContactInfo, COUNT(Interval) AS NumCalls
FROM AllCalls
GROUP BY ContactInfo
HAVING COUNT(AllCalls.ContactInfo) > 1
I'd like to have it return the date, the number of calls on that date if more than 1, and the phone number.
Sample data:
|ID |Interval |ContactInfo|
|--------|------------|-----------|
|1 |3/1/2017 |8009999999 |
|2 |3/1/2017 |8009999999 |
|3 |3/2/2017 |8001234567 |
|4 |3/2/2017 |8009999999 |
|5 |3/3/2017 |8007771111 |
|6 |3/3/2017 |8007771111 |
|--------|------------|-----------|
Expected result:
|Interval |ContactInfo|NumCalls|
|------------|-----------|--------|
|3/1/2017 |8009999999 |2 |
|3/3/2017 |8007771111 |2 |
|------------|-----------|--------|
Just as juergen d suggested, you should try to add Interval in your GROUP BY. Like so:
SELECT AC.ContactInfo
, AC.Interval
, COUNT(*) AS qnty
FROM AllCalls AS AC
GROUP BY AC.ContactInfo
, AC.Interval
HAVING COUNT(*) > 1
The code should like this :
select Interval , ContactInfo, count(ID) AS NumCalls from AllCalls group by Interval, ContactInfo having count(ID)>1;

SQL query to return a grouped result as a single row

If I have a jobs table like:
|id|created_at |status |
----------------------------
|1 |01-01-2015 |error |
|2 |01-01-2015 |complete |
|3 |01-01-2015 |error |
|4 |01-02-2015 |complete |
|5 |01-02-2015 |complete |
|6 |01-03-2015 |error |
|7 |01-03-2015 |on hold |
|8 |01-03-2015 |complete |
I want a query that will group them by date and count the occurrence of each status and the total status for that date.
SELECT created_at status, count(status), created_at
FROM jobs
GROUP BY created_at, status;
Which gives me
|created_at |status |count|
-------------------------------
|01-01-2015 |error |2
|01-01-2015 |complete |1
|01-02-2015 |complete |2
|01-03-2015 |error |1
|01-03-2015 |on hold |1
|01-03-2015 |complete |1
I would like to now condense this down to a single row per created_at unique date with some sort of multi column layout for each status. One constraint is that status is any one of 5 possible words but each date might not have one of every status. Also I would like a total of all statuses for each day. So desired results would look like:
|date |total |errors|completed|on_hold|
----------------------------------------------
|01-01-2015 |3 |2 |1 |null
|01-02-2015 |2 |null |2 |null
|01-03-2015 |3 |1 |1 |1
the columns could be built dynamically from something like
SELECT DISTINCT status FROM jobs;
with a null result for any day that doesn't contain any of that type of status. I am no SQL expert but am trying to do this in a DB view so that I don't have to bog down doing multiple queries in Rails.
I am using Postresql but would like to try to keep it straight SQL. I have tried to understand aggregate function enough to use some other tools but not succeeding.
The following should work in any RDBMS:
SELECT created_at, count(status) AS total,
sum(case when status = 'error' then 1 end) as errors,
sum(case when status = 'complete' then 1 end) as completed,
sum(case when status = 'on hold' then 1 end) as on_hold
FROM jobs
GROUP BY created_at;
The query uses conditional aggregation so as to pivot grouped data. It assumes that status values are known before-hand. If you have additional cases of status values, just add the corresponding sum(case ... expression.
Demo here
An actual crosstab query would look like this:
SELECT * FROM crosstab(
$$SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY 1, 2
ORDER BY 1, 2$$
,$$SELECT unnest('{error,complete,"on hold"}'::text[])$$)
AS ct (date date, errors int, completed int, on_hold int);
Should perform very well.
Basics:
PostgreSQL Crosstab Query
The above does not yet include the total per date.
Postgres 9.5 introduces the ROLLUP clause, which is perfect for the case:
SELECT * FROM crosstab(
$$SELECT created_at, COALESCE(status, 'total'), ct
FROM (
SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY created_at, ROLLUP(status)
) sub
ORDER BY 1, 2$$
,$$SELECT unnest('{total,error,complete,"on hold"}'::text[])$$)
AS ct (date date, total int, errors int, completed int, on_hold int);
Up to Postgres 9.4, use this query instead:
WITH cte AS (
SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY 1, 2
)
TABLE cte
UNION ALL
SELECT created_at, 'total', sum(ct)
FROM cte
GROUP BY 1
ORDER BY 1
Related:
Grouping() equivalent in PostgreSQL?
If you want to stick to a simple query, this is a bit shorter:
SELECT created_at
, count(*) AS total
, count(status = 'error' OR NULL) AS errors
, count(status = 'complete' OR NULL) AS completed
, count(status = 'on hold' OR NULL) AS on_hold
FROM jobs
GROUP BY 1;
count(status) for the total per date is error-prone, because it would not count rows with NULL values in status. Use count(*) instead, which is also shorter and a bit faster.
Here is a list of techniques:
For absolute performance, is SUM faster or COUNT?
In Postgres 9.4+ use the new aggregate FILTER clause, like #a_horse mentioned:
SELECT created_at
, count(*) AS total
, count(*) FILTER (WHERE status = 'error') AS errors
, count(*) FILTER (WHERE status = 'complete') AS completed
, count(*) FILTER (WHERE status = 'on hold') AS on_hold
FROM jobs
GROUP BY 1;
Details:
How can I simplify this game statistics query?

remove view duplicate data in sql database

SELECT ticket_id, thread_type, FROM ost_ticket_thread WHERE thread_type='M'
this is my sql statement. the result from the sql statement like below.
-----------------------
|ticket_id|thread_type|
-----------------------
|4 | M |
|4 | M |
|5 | M |
-----------------------
How i want to view data without the same ticket_id and thread_type?
i have done the group by statement. but i have encounter a problem when i want to count all ticket_id. it should be 2. but it return 3.
Try GROUP BY:
SELECT ticket_id, thread_type
FROM ost_ticket_thread
WHERE thread_type='M'
GROUP BY ticket_id,thread_type
OR
Use DISTINCT:
SELECT DISTINCT ticket_id, thread_type
FROM ost_ticket_thread
WHERE thread_type='M'
Result:
TICKET_ID THREAD_TYPE
4 M
5 M
Sample result in SQL Fiddle.
EDIT:
For the getting the count:
SELECT ticket_id, thread_type,COUNT(ticket_id) as Count
FROM ost_ticket_thread
WHERE thread_type='M'
GROUP BY ticket_id,thread_type
Result:
TICKET_ID THREAD_TYPE COUNT
4 M 2
5 M 1
Use this query
SELECT distinct(ticket_id), thread_type, FROM ost_ticket_thread WHERE thread_type='M';
this will work