Oracle SQL Count of a field Grouped by another field - sql

I have a small query below which i want to have a Count of each HOUSE_ID and Grouped By LOCATION_ID however it is not grouping by LOCATION_ID because the HOUSE_ID's are different. I want it to count HOUSE_ID's by LOCATION_ID's regardless of the HOUSE_ID.
QUERY
SELECT
COUNT(HOUSE_ID) AS Count,
LOCATION_ID,
ZONE,
AREA
FROM TABLE
WHERE SITE_ID = 'ABC'
AND LOCATION_ID NOT LIKE ('%LAND%')
GROUP BY LOCATION_ID, HOUSE_ID, ZONE, AREA
Expected Result
_____________________________
|Count|LOCATION_ID|ZONE|AREA|
|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯|¯¯¯¯|
| 4 | LOCA | 2 | 1 |
| 7 | LOCB | 6 | 2 |
| 3 | LOCC | 3 | 1 |
| 9 | LOCD | 5 | 7 |
| 6 | LOCE | 7 | 4 |
| 2 | LOCF | 2 | 1 |
| 8 | LOCG | 7 | 5 |
| 7 | LOCH | 9 | 1 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Actual Result
_____________________________
|Count|LOCATION_ID|ZONE|AREA|
|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯|¯¯¯¯|
| 1 | LOCA | 2 | 1 |
| 1 | LOCA | 6 | 2 |
| 1 | LOCA | 3 | 1 |
| 1 | LOCA | 5 | 7 |
| 1 | LOCA | 7 | 4 |
| 1 | LOCA | 2 | 1 |
| 1 | LOCA | 7 | 5 |
| 1 | LOCA | 9 | 1 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

You have to remove HOUSE_ID from group by clause.
Without source data I can only guess that you also need agregate functions for ZONE and AREA column, MAX fro example. Try below solution:
SELECT
COUNT(HOUSE_ID) AS Count,
LOCATION_ID,
MAX(ZONE),
MAX(AREA)
FROM TABLE
WHERE SITE_ID = 'ABC'
AND LOCATION_ID NOT LIKE ('%LAND%')
GROUP BY LOCATION_ID

Got it, Needed to Count(*)!!
SELECT
COUNT(*) AS Count,
SUM(AREA) AS AREA
LOCATION_ID,
ZONE,
FROM TABLE
WHERE SITE_ID = 'ABC'
AND LOCATION_ID NOT LIKE ('%LAND%')
GROUP BY LOCATION_ID, ZONE

Related

Postgresql: Group rows in a row and add array

Hi i have a table like this;
+----+----------+-------------+
| id | room_id | house_id |
+----+----------+-------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 1 | 2 |
| 5 | 2 | 2 |
| 6 | 3 | 2 |
| 7 | 1 | 3 |
| 8 | 2 | 3 |
| 9 | 3 | 3 |
+----+-------+----------------+
and i want to create a view like this
+----+----------+-------------+
| id | house_id | rooms |
+----+----------+-------------+
| 1 | 1 | [1,2,3] |
| 2 | 2 | [1,2,3] |
| 3 | 3 | [1,2,3] |
+----+-------+----------------+
i tried many ways but i cant gruop them in one line
Thanks for any help.
You can use array_agg():
select house_id, array_agg(room_id order by room_id) as rooms
from t
group by house_id;
If you want the first column to be incremental, you can use row_number():
select row_number() over (order by house_id) as id, . . .

SQL order by but repeat crescent numbers

I'm using SQL Server 2014 and i'm having a trouble with a query.
I have this scenario bellow:
| Number | Series | Name |
|--------|--------|---------|
| 9 | 1 | Name 1 |
| 5 | 3 | Name 2 |
| 8 | 2 | Name 3 |
| 7 | 3 | Name 4 |
| 0 | 1 | Name 5 |
| 1 | 2 | Name 6 |
| 9 | 2 | Name 7 |
| 3 | 3 | Name 8 |
| 4 | 1 | Name 9 |
| 0 | 1 | Name 10 |
and I need to get it ordered by series column like this:
| Number | Series | Name |
|--------|--------|---------|
| 9 | 1 | Name 1 |
| 8 | 2 | Name 3 |
| 5 | 3 | Name 2 |
| 7 | 1 | Name 5 |
| 1 | 2 | Name 6 |
| 0 | 3 | Name 4 |
| 4 | 1 | Name 9 |
| 9 | 2 | Name 7 |
| 3 | 3 | Name 8 |
| 0 | 1 | Name 10 |
Actually is more a sequency in "series" column than an ordenation.
1,2,3 again 1,2,3...
Somebody could help me?
You can do this using the ANSI standard function row_number():
select number, series, name
from (select t.*, row_number() over (partition by series order by number) as seqnum
from t
) t
order by seqnum, series;
This assigns "1" to the first record for each series, "2" to the second, and so on. The outer order by then puts all the "1"s together, all the "2" together. This has the effect of interleaving the values of the series.

Alternation of positive and negative values

thank you for attention.
I have a table called "PROD_COST" with 5 fields
(ID,Duration,Cost,COST_NEXT,COST_CHANGE).
I need extra field called "groups" for aggregation.
Duration = number of days the price is valid (1 day=1row).
Cost = product price in this day.
Cost_next = lead(cost,1,0).
Cost_change = Cost_next - Cost.
Now i need to group by Cost_change. It can be
positive,negative or 0 values.
+----+---+------+------+------+
| 1 | 1 | 10 | 8,5 | -1,5 |
| 2 | 1 | 8,5 | 12,2 | 3,7 |
| 3 | 1 | 12,2 | 5,3 | -6,9 |
| 4 | 1 | 5,3 | 4,2 | 1,2 |
| 5 | 1 | 4,2 | 6,2 | 2 |
| 6 | 1 | 6,2 | 9,2 | 3 |
| 7 | 1 | 9,2 | 7,5 | -2,7 |
| 8 | 1 | 7,5 | 6,2 | -1,3 |
| 9 | 1 | 6,2 | 6,3 | 0,1 |
| 10 | 1 | 6,3 | 7,2 | 0,9 |
| 11 | 1 | 7,2 | 7,5 | 0,3 |
| 12 | 1 | 7,5 | 0 | 7,5 |
+----+---+------+------+------+`
I need to make a query, which will group it by first negative or positive value (+ - + - + -). Last one field is what i want.
Sorry for my English `
+----+---+------+------+------+---+
| 1 | 1 | 10 | 8,5 | -1,5 | 1 |
| 2 | 1 | 8,5 | 12,2 | 3,7 | 2 |
| 3 | 1 | 12,2 | 5,3 | -6,9 | 3 |
| 4 | 1 | 5,3 | 4,2 | 1,2 | 4 |
| 5 | 1 | 4,2 | 6,2 | 2 | 4 |
| 6 | 1 | 6,2 | 9,2 | 3 | 4 |
| 7 | 1 | 9,2 | 7,5 | -2,7 | 5 |
| 8 | 1 | 7,5 | 6,2 | -1,3 | 5 |
| 9 | 1 | 6,2 | 6,3 | 0,1 | 6 |
| 10 | 1 | 6,3 | 7,2 | 0,9 | 6 |
| 11 | 1 | 7,2 | 7,5 | 0,3 | 6 |
| 12 | 1 | 7,5 | 0 | 7,5 | 6 |
+----+---+------+------+------+---+`
If you're in SQL Server 2012 you can use the window functions to do this:
select
id, COST_CHANGE, sum(GRP) over (order by id asc) +1
from
(
select
*,
case when sign(COST_CHANGE) != sign(isnull(lag(COST_CHANGE)
over (order by id asc),COST_CHANGE)) then 1 else 0 end as GRP
from
PROD_COST
) X
Lag will get the value from previous row, check the sign of it and compare it to the current row. If the values don't match, the case will return 1. The outer select will do a running total of these numbers, and every time there is 1, it will increase the total.
It is possible to use the same logic with older versions too, you'll just have to fetch the previous row from the table using the id and do running total by re-calculating all rows before the current one.
Example in SQL Fiddle
James's answer is close but it doesn't handle the zero value correctly. This is a pretty easy modification. One tricky approximation uses differences between product changes:
select id, COST_CHANGE, sum(IsNewGroup) over (order by id asc) +1
from (select pc.*,
(case when sign(cost_change) - sign(lag(cost_change) over (order by id)) between -1 and 1
then 0
else 1 -- `NULL` intentionally goes here
end) IsNewGroup
from Prod_Cost
) pc
For clarity, here is a SQL Fiddle with zero values. From my understanding of the question, the OP only wants an actual sign change.
This may still not be correct. The OP simply is not clear about what to do about 0 values.

Aggregate values by parents recursively in Oracle

Consider the the following example structure:
DEPARTMENT
ID
PARENT_ID
NAME
DEPTH
PROJECT
ID
NAME
COST
DEPARTMENT_ID
Some data, just for the sake of the examples bellow:
| ID | PARENT_ID | NAME | DEPTH |
|----|-----------|-------|-------|
| 1 | NULL | DEPT1 | 1 |
| 2 | 1 | DEPT2 | 2 |
| 3 | 1 | DEPT3 | 2 |
| 4 | 2 | DEPT4 | 3 |
| 5 | 3 | DEPT5 | 3 |
| 6 | NULL | DEPT6 | 1 |
| 7 | 6 | DEPT7 | 2 |
| ID | NAME | COST | DEPARTMENT_ID |
|------|--------|-------|---------------|
| 1 | PRJ1 | 100 | 1 |
| 2 | PRJ2 | 200 | 2 |
| 3 | PRJ3 | 300 | 3 |
| 4 | PRJ4 | 400 | 4 |
| 5 | PRJ5 | 500 | 5 |
| 6 | PRJ6 | 600 | 6 |
| 7 | PRJ7 | 700 | 7 |
Now, I need to somehow aggregate the costs of the, projects by one department and then by its direct children.
If the choosen filter is DEPT1, the intented result is:
| LINE | DEPARTMENT_ID | PARENT_ID | NAME | AGGREGATE_COST |
|------|----------------|-----------|--------|----------------|
| 1 | 1 | NULL | DEPT1 | 1500 |
| 2 | 2 | 1 | DEPT2 | 600 |
| 3 | 3 | 1 | DEPT3 | 800 |
Where:
Line 3 aggregate is PRJ5 (of DEPT5, which is child of DEPT3) + PRJ3 (of DEPT3) cost
Line 2 aggregate is PRJ4 (of DEPT4, which is child of DEPT2) + PRJ2 (of DEPT2) cost
Line 1 aggregate is the sum of his childrens aggregates.
PRJ6 and PRJ7 costs are ignored because the are from DEPT6 and DEPT7, and those are not in the hierachy of DEPT1 (DEPT6 would be his sibling, not child)
EDIT:
| ID | NAME | COST | DEPARTMENT_ID |
|------|--------|-------|---------------|
| 1 | PRJ1 | 1 | 1 |
| 2 | PRJ2 | 1 | 1 |
| 3 | PRJ3 | 1 | 2 |
| 4 | PRJ4 | 1 | 2 |
| 5 | PRJ5 | 1 | 4 |
In this scenario, the solution ivanzg presented, doesn't seem to work.
I get doubled results for the projects in the highers ranks
If I get the aggregate for DEPT1, it returns something similar to this:
| LINE | DEPARTMENT_ID | PARENT_ID | NAME | AGGREGATE_COST |
|------|----------------|-----------|--------|----------------|
| 1 | 1 | NULL | DEPT1 | 8 |
| 2 | 2 | NULL | DEPT1 | 4 |
You can tag rows in a hierarchy query (to later create groups) by using CONNECT_BY_ROOT hierarchy operator. In the hierarchy query, by making all rows root rows you create every hierarchy combination, later only specified combinations are taken and aggregated. For your test data this returns what you specified.
SELECT ROOT_DEPT AS DEPARTMENT_ID
,ROOT_PARENT AS PARENT_ID
,ROOT_NAME AS NAME
,SUM(COST) AS AGGREGATE_COST
FROM (SELECT COST
,CONNECT_BY_ROOT DEPARTMENT_ID ROOT_DEPT
,CONNECT_BY_ROOT PARENT_ID ROOT_PARENT
,CONNECT_BY_ROOT NAME ROOT_NAME
FROM (SELECT B.DEPARTMENT_ID
,NVL(A.PARENT_ID,'0') PARENT_ID
,A.NAME
,SUM(B.COST) COST
FROM DEPARTMENT A
JOIN PROJECT B
ON A.ID = B.DEPARTMENT_ID
--> GROUP COST OF PROJECTS IN THE SAME DEPARTMENT IF THERE ARE ANY
GROUP BY B.DEPARTMENT_ID
,NVL(A.PARENT_ID,'0')
,A.NAME
)
--> MAKE ALL ROWS ROOT ROWS
CONNECT BY PRIOR DEPARTMENT_ID = PARENT_ID
)
WHERE ROOT_DEPT = 1 OR ROOT_PARENT = 1
GROUP BY ROOT_DEPT
,ROOT_PARENT
,ROOT_NAME

Oracle rank function issue

Iam experiencing an issue in oracle analytic functions
I want the rank in oracle to be displayed sequentialy but require a cyclic fashion.But this ranking should happen within a group.
Say I have 10 groups
In 10 groups each group must be ranked in till 9. If greater than 9 the rank value must start again from 1 and then end till howmuch so ever
emp id date1 date 2 Rank
123 13/6/2012 13/8/2021 1
123 14/2/2012 12/8/2014 2
.
.
123 9/10/2013 12/12/2015 9
123 16/10/2013 15/10/2013 1
123 16/3/2014 15/9/2015 2
In the above example the for the group of rows of the empid 123 i have split the rank in two subgroup fashion.Sequentially from 1 to 9 is one group and for the rest of the rows the rank again starts from 1.How to achieve this in oracle rank functions.
as per suggestion from Egor Skriptunoff above:
select
empid, date1, date2
, row_number() over(order by date1, date2) as "rank"
, mod(row_number() over(order by date1, date2)-1, 9)+1 as "cycle_9"
from yourtable
example result
| empid | date1 | date2 | rn | ranked |
|-------|----------------------|----------------------|----|--------|
| 72232 | 2016-10-26T00:00:00Z | 2017-03-07T00:00:00Z | 1 | 1 |
| 04365 | 2016-11-03T00:00:00Z | 2017-07-29T00:00:00Z | 2 | 2 |
| 79203 | 2016-12-15T00:00:00Z | 2017-05-16T00:00:00Z | 3 | 3 |
| 68638 | 2016-12-18T00:00:00Z | 2017-02-08T00:00:00Z | 4 | 4 |
| 75784 | 2016-12-24T00:00:00Z | 2017-11-18T00:00:00Z | 5 | 5 |
| 72836 | 2016-12-24T00:00:00Z | 2018-09-10T00:00:00Z | 6 | 6 |
| 03679 | 2017-01-24T00:00:00Z | 2017-10-14T00:00:00Z | 7 | 7 |
| 43527 | 2017-02-12T00:00:00Z | 2017-01-15T00:00:00Z | 8 | 8 |
| 03138 | 2017-02-26T00:00:00Z | 2017-01-30T00:00:00Z | 9 | 9 |
| 89758 | 2017-03-29T00:00:00Z | 2018-04-12T00:00:00Z | 10 | 1 |
| 86377 | 2017-04-14T00:00:00Z | 2018-10-07T00:00:00Z | 11 | 2 |
| 49169 | 2017-04-28T00:00:00Z | 2017-04-21T00:00:00Z | 12 | 3 |
| 45523 | 2017-05-03T00:00:00Z | 2017-05-07T00:00:00Z | 13 | 4 |
SQL Fiddle