several conditions in the same sql query (having and min) - sql

I have a table with these variables and I want to select the row where sum of the VALUE is greater than 1000 and at the same time the MONTH is the minimum
|CLIENT |MONTH |VALUE
|1 |1 |500
|1 |2 |1050
|1 |3 |1100
the result should be this:
|CLIENT|MONTH|VALUE
|1 |2 |1050
Is it possible to do it in only one query?
My attempt:
SELECT
client,
SUM(value) AS SUM_of_value,
MIN(month) AS MIN_of_month,
FROM mytable
GROUP BY 1
having SUM_of_value>1000;

You coud try using a join between your query and the query group by client and month
select t.client, t2.SUM_of_value, t2.MIN_of_month
from (
SELECT
client,
SUM(value) AS SUM_of_value,
MIN(month) AS MIN_of_month,
FROM mytable
GROUP BY 1
having SUM_of_value>1000;
) t1
inner join (
SELECT
client,
month ,
SUM(value) AS SUM_of_value
FROM mytable
GROUP BY 1,2
having SUM_of_value>1000;
) t2 ON t2.client = t1.client
AND t2.month = t1.MIN_of_month

If you want one row, then you can use order by:
select t.*
from mytable
where value > 1000
order by month
fetch first 1 row only;
I'm not sure why your query refers to "sum of value", because there is only one value per month for the data in the question.
EDIT:
Based on the comment, you do seem to want an aggregation query:
select client, month, sum(value)
from
group by client, month
having sum(value) > 1000
order by month
fetch first 1 row only;

Borrowing from Gordon and Forpas. Your sample data and output seems too simplistic. I think you would want one row per client.
select t.client, t.month, t.value
from (
select client, month, sum(value) as value, row_number() over (partition by client order by month) rn
from my_table
group by client, month
having sum(value) > 1000
) t
where t.rn = 1

If there is only 1 VALUE per each MONTH then use ROW_NUMBER() window function:
select t.CLIENT, t.MONTH, t.VALUE
from (
select *, row_number() over (partition by client order by month) rn
from mytable
where value > 1000
) t
where t.rn = 1
See the demo.
Results:
| CLIENT | MONTH | VALUE |
| ------ | ----- | ----- |
| 1 | 2 | 1050 |

Related

Distinct particular field in select query

I have table with below sample values.
|Id|Keyword|insertedon|
|:-|:------|:---------|
|1 | abcd | 13/12/20 |
|2 | cdef | 14/12/20 |
|3 | abcd | 14/12/20 |
|4 | defg | 14/12/20 |
In the above table i need distinct values of keywords order by insertedon desc order.
I need recent top 5 results.
Expected Result:
defc
abcd
cdef
Please let me know how to achieve this.
You get the top 5 results with TOP(5) in SQL Server. You'd order the keywords by their last insertedon date:
select top(5) keyword
from mytable
group by keyword
order by max(insertedon) desc;
If you are looking for latest entries based on insertedon column, you can find using the group by clause, something like this:
select keyword, max(insertedon)
from table
group by keyword
order by 2 desc
You can just use select distinct:
select distinct keyword
from t;
If you wanted a full row, you could use row_number():
select t.*
from (select t.*,
row_number() over (partition by keyword order by newid()) as seqnum
from t
) t
where seqnum = 1;
EDIT:
For the edited version, you can use:
select distinct keyword
from (select top (5) keyword
from t
order by insertedon desc
) k
Give a row number based on the descending order of the date column and then select the row wth row number 1.
Query
;with cte as(
select [rn] = row_number() over(
partition by [keyword]
order by [insertedon] desc, [id] desc
)
)
select [keyword] from cte
where [rn] = 1;
You can use the analytical functions as follows:
select t.* from
(select t.*,
row_number() over (partition by keyword order by insertedon desc) as rn,
Dense_rank() over (order by insertedon desc) as dr
from t ) t where rn = 1 and dr <= 5;

Min Date from one column multiple rows

My apologies, I should have added every column and complete problem not just portion.
I have a table A which stores all invoices issued(id 1) payments received (id 4) from clients. Sometimes client pay in 2-3 installments. I want to find dateifference between invoice issued and last payment collected for the invoice. My data looks like this
**a.cltid**|**A.Invnum**|A.Cash|A.Date | a.type| a.status
70 |112 |-200 |2012-03-01|4 |P
70 |112 |-500 |2012-03-12|4 |P
90 |124 |-550 |2012-01-20|4 |P
70 |112 |700 |2012-02-20|1 |p
55 |101 |50 |2012-01-15|1 |d
90 |124 |550 |2012-01-15|1 |P
I am running
Select *, Datediff(dd,T.date,P.date)
from (select a.cltid, a.invnumber,a.cash, min(a.date)date
from table.A as A
where a.status<>'d' and a.type=1
group by a.cltid, a.invnumber,a.cash)T
join
Select *
from (select a.cltid, a.invnumber,a.cash, min(a.date)date
from table.A as A
where a.status<>'d' and a.type=4
group by a.cltid, a.invnumber,a.cash)P
on
T.invnumb=P.invnumber and T.cltid=P.cltid
How can I make it work? So it shows me
70|112|-500|2012-03-12|4|P 70|112|700|2012-02-20|1|p|22
90|124|-550|2012-01-20|4|P 90|124|550|2012-01-15|1|P|5
Edited***
You can use row_number to assign sequence number within each cltid in the order of decreasing date and then filter to get the first row for each cltid which will be the row with latest date for that cltid:
select *
from (
select A.*,
row_number() over (
partition by a.cltid order by a.date desc
) rn
from table.A as A
) t
where rn = 1;
It will return one row (with latest date) for each client. If you want to return all the rows which have latest date, use rank() instead.
Use a ranking function to get all the columns:
select a.*
from (select a.*,
row_number() over (partition by cltid order by date desc) as seqnum
from a
) a
where seqnum = 1;
Use aggregation if you only want the date. The issue with your query is that the group by clause has too many columns:
select a.cltid, max(a.date) as date
from table.A as A
group by a.cltid;
And the fact that min() returns the first date not the last date.
There are many ways to do this. Here are some of them:
test setup: http://rextester.com/VGUY60367
with common_table_expression as () using row_number()
with cte as (
select *
, rn = row_number() over (
partition by cltid, Invnum
order by [date] desc
)
from a
)
select cltid, Invnum, Cash, [date]
from cte
where rn = 1
cross apply version:
select distinct
a.cltid
, a.Invnum
, x.Cash
, x.[date]
from a
cross apply (
select top 1
cltid, Invnum
, [date]
, Cash
from a as i
where i.cltid =a.cltid
and i.Invnum=a.Invnum
order by i.[date] desc
) as x;
top with ties version:
select top 1 with ties
*
from a
order by
row_number() over (
partition by cltid, Invnum
order by [date] desc
)
all return:
+-------+--------+---------------------+------+
| cltid | Invnum | date | Cash |
+-------+--------+---------------------+------+
| 70 | 112 | 12.03.2012 00:00:00 | -500 |
| 90 | 124 | 20.01.2012 00:00:00 | -550 |
+-------+--------+---------------------+------+
You can achieve the desired o/p by this:
Select
a.cltid, a.invnumber,a.cash, max(a.date) [date]
from
YourTable a
group by
a.cltid, a.invnumber, a.cash, a.date

Comparing row values in oracle

I have Table1 with three columns:
Key | Date | Price
----------------------
1 | 26-May | 2
1 | 25-May | 2
1 | 24-May | 2
1 | 23 May | 3
1 | 22 May | 4
2 | 26-May | 2
2 | 25-May | 2
2 | 24-May | 2
2 | 23 May | 3
2 | 22 May | 4
I want to select the row where value 2 was last updated (24-May). The Date was sorted using RANK function.
I am not able to get the desired results. Any help will be appreciated.
SELECT *
FROM (SELECT key, DATE, price,
RANK() over (partition BY key order by DATE DESC) AS r2
FROM Table1 ORDER BY DATE DESC) temp;
Another way of looking at the problem is that you want to find the most recent record with a price different from the last price. Then you want the next record.
with lastprice as (
select t.*
from (select t.*
from table1 t
order by date desc
) t
where rownum = 1
)
select t.*
from (select t.*
from table1 t
where date > (select max(date)
from table1 t2
where t2.price <> (select price from lastprice)
)
order by date asc
) t
where rownum = 1;
This query looks complicated. But, it is structured so it can take advantage of indexes on table1(date). The subqueries are necessary in Oracle pre-12. In the most recent version, you can use fetch first 1 row only.
EDIT:
Another solution is to use lag() and find the most recent time when the value changed:
select t1.*
from (select t1.*
from (select t1.*,
lag(price) over (order by date) as prev_price
from table1 t1
) t1
where prev_price is null or prev_price <> price
order by date desc
) t1
where rownum = 1;
Under many circumstances, I would expect the first version to have better performance, because the only heavy work is done in the innermost subquery to get the max(date). This verson has to calculate the lag() as well as doing the order by. However, if performance is an issue, you should test on your data in your environment.
EDIT II:
My best guess is that you want this per key. Your original question says nothing about key, but:
select t1.*
from (select t1.*,
row_number() over (partition by key order by date desc) as seqnum
from (select t1.*,
lag(price) over (partition by key order by date) as prev_price
from table1 t1
) t1
where prev_price is null or prev_price <> price
order by date desc
) t1
where seqnum = 1;
You can try this:-
SELECT Date FROM Table1
WHERE Price = 2
AND PrimaryKey = (SELECT MAX(PrimaryKey) FROM Table1
WHERE Price = 2)
This is very similar to the second option by Gordon Linoff but introduces a second windowed function row_number() to locate the most recent row that changed the price. This will work for all or a range of keys.
select
*
from (
select
*
, row_number() over(partition by Key order by [date] DESC) rn
from (
select
*
, NVL(lag(Price) over(partition by Key order by [date] DESC),0) prevPrice
from table1
where Key IN (1,2,3,4,5) -- as an example
)
where Price <> prevPrice
)
where rn = 1
apologies but I haven't been able to test this at all.

How to select max of count in PostgreSQL

I have table in PostgreSQL with the following schema:
Category | Type
------------+---------
A | 0
C | 11
B | 5
D | 1
D | 0
F | 2
E | 11
E | 9
. | .
. | .
How can I select category wise maximum occurrence of type? The following give me all:
SELECT
category,
type,
COUNT(*)
FROM
table
GROUP BY
category,
type
ORDER BY
category,
count
DESC
My expected result is something like this:
Cat |Type |Count
--------+-------+------
A |0 |5
B |5 |30
C |2 |20
D |3 |10
That is the type with max occurrence in each category with count of that type.
You can use the following query:
SELECT category, type, cnt
FROM (
SELECT category, type, cnt,
RANK() OVER (PARTITION BY category
ORDER BY cnt DESC) AS rn
FROM (
SELECT category, type, COUNT(type) AS cnt
FROM mytable
GROUP BY category, type ) t
) s
WHERE s.rn = 1
The above query uses your own query as posted in the OP and applies RANK() windowed function to it. Using RANK() we can specify all records coming from the initial query having the greatest COUNT(type) value.
Note: If there are more than one types having the maximum number of occurrences for a specific category, then all of them will be returned by the above query, as a consequence of using RANK.
Demo here
If I understand correctly, you can use window functions:
SELECT category, type, cnt
FROM (SELECT category, type, COUNT(*) as cnt,
ROW_NUMBER() OVER (PARTITION BY type ORDER BY COUNT(*) DESC) as seqnum
FROM table
GROUP BY category, type
) ct
WHERE seqnum = 1;
SELECT
category,
type,
COUNT(*)
FROM
table
GROUP BY
category,
type
HAVING
COUNT(*) = (SELECT MAX(C) FROM (SELECT COUNT(*) AS C FROM A GROUP BY A) AS Q)
EDITED:
I apologize to readers,
COUNT(*) = (SELECT MAX(COUNT(*)) FROM table GROUP BY category,type)
is the ORACLE version, postgresql version is:
COUNT(*) = (SELECT MAX(C) FROM (SELECT COUNT(*) AS C FROM A GROUP BY A) AS Q)
SELECT category , MAX (Occurence)
FROM (SELECT t.category as category , Count(*) AS Occurence FROM table t);
SELECT
category,
type,
COUNT(*) AS count
FROM
table
GROUP BY
category,
type
ORDER BY
category ASC

Identifying the boundaries of N-groups

I have spent quite some time dealing with the following:
Imagine that you have N number of groups with multiple records each and every record has unique starting and ending points.
In other words:
ID|GroupName|StartingPoint|EndingPoint|seq(row_number)|desired_seq
__|_________|_____________|___________|_______________|____________
1 | Grp1 |2014-01-06 |2014-01-07 |1 |1
__|_________|_____________|___________|_______________|____________
2 | Grp1 |2014-01-07 | 2014-01-08|2 |2
__|_________|_____________|___________|_______________|____________
3 | Grp2 |2014-01-08 | 2014-01-09|1 |1
__|_________|_____________|___________|_______________|____________
4 | Grp1 |2014-01-09 | 2014-01-10|3 |1
__|_________|_____________|___________|_______________|____________
5 | Grp2 |2014-01-10 | 2014-01-11|2 |1
__|_________|_____________|___________|_______________|____________
As you can see, the starting point for every consecutive record is the same as the ending point of the previous.
Basically, I would like to obtain the minimumS and maximumS for each group based on the dates. Once a record with new group name appears, then consider it as a new group and reset the sequencing.
Single row_number() function is not sufficient enough for this task since it doesnt reflect the change in the group names.(I have included a seq column in the sample data which represents the values generated by row number)
Desired result based on the sample data:
1 Grp1 |2014-01-06 | 2014-01-08
2 Grp2 |2014-01-08 | 2014-01-09
3 Grp1 |2014-01-09 | 2014-01-10
4 Grp2 |2014-01-10 | 2014-01-11
What I have tried:
;with cte as(
select *
, row_number() over (partition by GroupName order by startingpoint) as seq
from table1
)
select *
into #temp2
from cte t1
left join cte t2 on t1.id=t2.id and t1.seq= t2.seq-1
select *
,(select startingPoint from #temp2 t2 where t1.id=t2.id and t2.seq= (select MIN(seq) from #temp2) as Oldest
(select startingPoint from #temp2 t2 where t1.id=t2.id and t2.seq= (select MAX(seq) from #temp2) as MostRecent
from #temp2 t1
This is a gaps-and-islands problem with subgrouping. The trick is grouping by the difference between two ROW_NUMBER() values, one partitioned and one unpartitioned.
WITH t AS (
SELECT
GroupName,
StartingPoint,
EndingPoint,
ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY StartingPoint)
- ROW_NUMBER() OVER(ORDER BY StartingPoint) AS SubGroupId
FROM #test
)
SELECT
ROW_NUMBER() OVER (ORDER BY MIN(StartingPoint)) AS SortOrderId,
GroupName AS GroupName,
MIN(StartingPoint) AS GroupStartingPoint,
MAX(EndingPoint) AS GroupEndingPoint
FROM t
GROUP BY GroupName, SubGroupId
ORDER BY SortOrderId
This is so much easier with the lag() functionality in SQL Server 2012. The way I approach these problems is to find where groups start, assigning a flag of 1 or 0 to each row. Then take a cumulative sum of the 1s to get a new group id.
In SQL Server 2008, you can do this with correlated subqueries (or joins):
with table1_flag as (
select t1.*,
isnull((select top 1 1
from table1 t2
where t2.groupname = t1.groupname and
t2.endingpoint = t1.startingpoint
), 0) as groupstartflag
from table1 t1
),
table1_flag_cum as (
select tf.*,
(select sum(groupstartflag)
from table1_flag tf2
where tf2.groupname = tf.groupname and
tf2.startingpoint <= tf.startingpoint
) as groupnum
from table1_flag tf
)
select groupnum, groupname,
min(startingpoint) as startingpoint, max(endingpoint) as endingpoint
from table1_flag_cum
group by groupnum, groupname;
Not sure, but maybe:
SELECT DISTINCT
GroupName,
MIN(StartingPoint) OVER (PARTITION BY GroupName ORDER BY Id),
MAX(EndingPoint) OVER (PARTITION BY GroupName ORDER BY Id)
FROM table1
Because partition does not lead to the reduction of number of rows there will be originally duplicated entries, which are removed with distinct.