sql (oracle) counting number of overlapping intervals - sql

I have the following problem:
Given the following table test in an oracle sql database:
+----+------+-------+------+
| id | name | start | stop |
+----+------+-------+------+
| 1 | A | 1 | 5 |
+----+------+-------+------+
| 2 | A | 2 | 6 |
+----+------+-------+------+
| 3 | A | 5 | 8 |
+----+------+-------+------+
| 4 | A | 9 | 10 |
+----+------+-------+------+
| 5 | B | 3 | 6 |
+----+------+-------+------+
| 6 | B | 4 | 8 |
+----+------+-------+------+
| 7 | B | 1 | 2 |
+----+------+-------+------+
I would like to find the number of overlapping intervals (endpoints included) [start, stop] n_overlap, for all id having the same name, i.e.:
+----+------+-------+------+-----------+
| id | name | start | stop | n_overlap |
+----+------+-------+------+-----------+
| 1 | A | 1 | 5 | 3 |
+----+------+-------+------+-----------+
| 2 | A | 2 | 6 | 3 |
+----+------+-------+------+-----------+
| 3 | A | 4 | 8 | 3 |
+----+------+-------+------+-----------+
| 4 | A | 9 | 10 | 1 |
+----+------+-------+------+-----------+
| 5 | B | 3 | 6 | 2 |
+----+------+-------+------+-----------+
| 6 | B | 4 | 8 | 2 |
+----+------+-------+------+-----------+
| 7 | B | 1 | 2 | 1 |
+----+------+-------+------+-----------+

One method uses a correlated subquery:
select t.*,
(select count(*)
from test t2
where t2.name = t.name and
t2.start < t.end and
t2.end > t.start
) as num_overlaps
from test t;

Related

Find relation level from text field

I have a table containing geographical structure of units. There are parent-child relation columns but I want to use the existing text field (instead of recursion) to find the relation level between the items.
(here's a table creation script)
drop table if exists #temp_structure
create table #temp_structure
(org_id int,
parent_org_id int,
org_name nvarchar(255),
search_tree nvarchar(255))
insert into #temp_structure
values
(1,null,'World','| 1 |'),
(2,1,'Europe','| 1 | 2 |'),
(3,1,'North America','| 1 | 3 |'),
(4,1,'South America','| 1 | 4 |'),
(5,1,'Asia','| 1 | 5 |'),
(6,1,'Africa','| 1 | 6 |'),
(7,1,'Australia','| 1 | 7 |'),
(8,2,'Spain','| 1 | 2 | 8 |'),
(9,2,'Germany','| 1 | 2 | 9 |'),
(10,2,'Italy','| 1 | 2 | 10 |'),
(11,2,'France','| 1 | 2 | 11 |'),
(12,8,'Madrid ','| 1 | 2 | 8 | 12 |'),
(13,8,'Barcelona ','| 1 | 2 | 8 | 13 |'),
(14,9,'Berlin','| 1 | 2 | 9 | 14 |'),
(15,9,'Munich','| 1 | 2 | 9 | 15 |'),
(16,10,'Rome','| 1 | 2 | 10 | 16 |'),
(17,10,'Milano','| 1 | 2 | 10 | 17 |'),
(18,11,'Paris','| 1 | 2 | 11 | 18 |'),
(19,11,'Marseille','| 1 | 2 | 11 | 19 |')
The expected result I would like to achieve is presented below (I listed only one 4th level example):
+--------+-------------+------------+
| org_id | search_item | nest_level |
+--------+-------------+------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 1 |
| 3 | 1 | 2 |
| 4 | 4 | 1 |
| 4 | 1 | 2 |
| 5 | 5 | 1 |
| 5 | 1 | 2 |
| 6 | 6 | 1 |
| 6 | 1 | 2 |
| 7 | 7 | 1 |
| 7 | 1 | 2 |
| 8 | 8 | 1 |
| 8 | 2 | 2 |
| 8 | 1 | 3 |
| 9 | 9 | 1 |
| 9 | 2 | 2 |
| 9 | 1 | 3 |
| 10 | 10 | 1 |
| 10 | 2 | 2 |
| 10 | 1 | 3 |
| 11 | 11 | 1 |
| 11 | 2 | 2 |
| 11 | 1 | 3 |
| 12 | 12 | 1 |
| 12 | 8 | 2 |
| 12 | 2 | 3 |
| 12 | 1 | 4 |
.....................................
+--------+-------------+------------+
I was able to pull the org_id-search_item relation using STRING_SPLIT, but I still miss the tricky level part (I wonder about enumerating the '|' characters)
SELECT t.org_id
--,substring(replace(search_tree, ' ', ''), 2, len(replace(search_tree, ' ', '')) - 2)
,ss.value as search_item
FROM #temp_structure t
CROSS APPLY string_split(substring(replace(search_tree, ' ', ''), 2, len(replace(search_tree, ' ', '')) - 2),'|') ss
I have not thoroughly tested this, but you could try something like the following:
-- Table mock-up.
DECLARE #temp TABLE ( org_id int, parent_org_id int, org_name nvarchar(255), search_tree nvarchar(255) )
-- Insert sample data...
INSERT INTO #temp VALUES
(1,null,'World','| 1 |'),(2,1,'Europe','| 1 | 2 |'),
(3,1,'North America','| 1 | 3 |'),(4,1,'South America','| 1 | 4 |'),
(5,1,'Asia','| 1 | 5 |'),(6,1,'Africa','| 1 | 6 |'),
(7,1,'Australia','| 1 | 7 |'),(8,2,'Spain','| 1 | 2 | 8 |'),
(9,2,'Germany','| 1 | 2 | 9 |'),(10,2,'Italy','| 1 | 2 | 10 |'),
(11,2,'France','| 1 | 2 | 11 |'),(12,8,'Madrid ','| 1 | 2 | 8 | 12 |');
-- Select data in a nested level...
SELECT
org_id,
search_item,
ROW_NUMBER() OVER ( PARTITION BY org_id ORDER BY org_id, parent_org_id, search_item DESC ) AS nest_level
FROM #temp AS tmp
CROSS APPLY (
SELECT CAST ( [value] AS INT ) AS search_item FROM STRING_SPLIT ( tmp.search_tree, '|' )
WHERE NULLIF ( [value], '' ) IS NOT NULL
) AS tree
ORDER BY
org_id, parent_org_id, search_item DESC;
Returns
+--------+-------------+------------+
| org_id | search_item | nest_level |
+--------+-------------+------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 1 |
| 3 | 1 | 2 |
| 4 | 4 | 1 |
| 4 | 1 | 2 |
| 5 | 5 | 1 |
| 5 | 1 | 2 |
| 6 | 6 | 1 |
| 6 | 1 | 2 |
| 7 | 7 | 1 |
| 7 | 1 | 2 |
| 8 | 8 | 1 |
| 8 | 2 | 2 |
| 8 | 1 | 3 |
| 9 | 9 | 1 |
| 9 | 2 | 2 |
| 9 | 1 | 3 |
| 10 | 10 | 1 |
| 10 | 2 | 2 |
| 10 | 1 | 3 |
| 11 | 11 | 1 |
| 11 | 2 | 2 |
| 11 | 1 | 3 |
| 12 | 12 | 1 |
| 12 | 8 | 2 |
| 12 | 2 | 3 |
| 12 | 1 | 4 |
+--------+-------------+------------+

writing SQL query to show result in specific order

I have this table
+----+--------+------------+-----------+
| Id | day_id | subject_id | period_Id |
+----+--------+------------+-----------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 8 | 2 | 6 | 1 |
| 9 | 2 | 7 | 2 |
| 15 | 3 | 3 | 1 |
| 16 | 3 | 4 | 2 |
| 22 | 4 | 5 | 1 |
| 23 | 4 | 5 | 2 |
| 24 | 4 | 6 | 3 |
| 29 | 5 | 8 | 1 |
| 30 | 5 | 1 | 2 |
to something like this
| Id | day_id | subject_id | period_Id |
| 1 | 1 | 1 | 1 |
| 8 | 2 | 6 | 1 |
| 15 | 3 | 3 | 1 |
| 22 | 4 | 5 | 1 |
| 29 | 5 | 8 | 1 |
| 2 | 1 | 2 | 2 |
| 2 | 1 | 2 | 2 |
| 16 | 3 | 4 | 2 |
| 23 | 4 | 5 | 2 |
| 30 | 5 | 1 | 2 |
+----+--------+------------+-----------+
SO, I want to choose one period with a different subject each day and doing this for number of weeks. so first subject dose not come until all subject have been chosen.
You can ORDER BY period_id first and then by day_id:
SELECT *
FROM your_table
ORDER BY period_Id, day_Id
LiveDemo

Sorting string columns in SQL

I already have the argument for the st column..
mysql> SELECT * FROM t ORDER BY LEFT(st,LOCATE(' ',st)), CAST(SUBSTRING(st,LOCATE(' ',st)+1) AS SIGNED);
+----+------+
| id | st |
+----+------+
| 1 | a 1 |
| 3 | a 6 |
| 4 | a 11 |
| 2 | a 11 |
| 5 | b 1 |
| 7 | b 6 |
| 8 | b 12 |
| 6 | b 12 |
+----+------+
this is what it should happen..
+----+------+
| id | st |
+----+------+
| 1 | a 1 |
| 3 | a 6 |
| 2 | a 11 |
| 4 | a 11 |
| 5 | b 1 |
| 7 | b 6 |
| 6 | b 12 |
| 8 | b 12 |
+----+------+
can I do this or not via SQL?
Well from what you show the only difference is that "ties" are broken by the ID column. If that's the case you can just use that as the third sort field:
SELECT *
FROM t
ORDER BY LEFT(st,LOCATE(' ',st)),
CAST(SUBSTRING(st,LOCATE(' ',st)+1) AS SIGNED),
ID

Ask about query in sql server

i have table like this:
| ID | id_number | a | b |
| 1 | 1 | 0 | 215 |
| 2 | 2 | 28 | 8952 |
| 3 | 3 | 10 | 2000 |
| 4 | 1 | 0 | 215 |
| 5 | 1 | 0 |10000 |
| 6 | 3 | 10 | 5000 |
| 7 | 2 | 3 |90933 |
I want to sum a*b where id_number is same, what the query to get all value for every id_number? for example the result is like this :
| ID | id_number | result |
| 1 | 1 | 0 |
| 2 | 2 | 523455 |
| 3 | 3 | 70000 |
This is a simple aggregation query:
select id_number, sum(a*b)
from t
group by id_number
I'm not sure what the first column is for.

trying to write a query with Table A,B where Cond1: A.pc=B.pc & Cond2: (preferred (A.sub = B.Sub) or else any 1 row that meet only Cond1)

I trying to get the result table to contain rows where
Condition1: A.pc=B.pc AND
Condition2: (preferred (A.sub = B.Sub) or
else any one row that satisfy only Condition1)
I have tried the following inner join query and few other join and sub-query but can not figure out exact way to write a query with above strange condition.
SELECT * FROM tblA AS A INNER JOIN tblB AS B
ON A.sub=B.sub
WHERE A.pc=B.pc
tblA
-------------------
| id | pc | sub |
-------------------
| 0 | 5 | abc |
| 1 | 8 | def |
| 2 | 6 | ghi |
| 3 | 2 | jkl |
| 4 | 7 | mno |
| 5 | 19 | pqr |
-------------------
tblB
-------------------------
| pc | sub | uml | ull |
-------------------------
| 3 |arm | 1 | 1 |
| 3 |gtk | 1 | 2 |
| 3 |lmn | 1 | 3 |
| 3 |pop | 1 | 4 |
| 5 |abc | 1 | 5 |
| 5 |hlq | 1 | 6 |
| 5 |pon | 2 | 1 |
| 5 |qrt | 2 | 2 |
| 7 |alo | 2 | 3 |
| 7 |mno | 2 | 4 |
| 7 |ghm | 2 | 5 |
| 7 |stm | 2 | 6 |
| 9 |mck | 2 | 7 |
| 9 |plo | 3 | 1 |
| 9 |rtk | 3 | 2 |
| 9 |ert | 3 | 3 |
| 6 |gji | 3 | 4 |
| 6 |ghi | 3 | 5 |
| 6 |yux | 4 | 1 |
| 6 |del | 4 | 2 |
| 2 |jkl | 4 | 3 |
| 2 |jll | 5 | 4 |
| 2 |uin | 6 | 1 |
| 2 |tro | 6 | 2 |
| 19 |ppm | 6 | 3 |
| 19 |kde | 6 | 4 |
| 19 |grp | 6 | 5 |
| 19 |sho | 6 | 6 |
-------------------------
Expected Result Table:
-------------------------------
| id | pc | sub | uml | ull |
-------------------------------
| 0 | 5 |abc | 1 | 5 |
| 2 | 6 |ghi | 3 | 5 |
| 3 | 2 |jkl | 4 | 3 |
| 4 | 7 |mno | 2 | 4 |
| 5 | 19 |ppm | 6 | 3 | *
-------------------------------
* notice this is a arbitrary row as (A.sub=B.sub) not found
** notice there is no result for id=1 as pc=8 do not exist in tblB
Until someone comes up with a better answer, here is some code that does what you want.
Please, note it might not be a good solution in terms of performance (espcially as your tables grow).
SELECT *
FROM (
SELECT tblA.id, tblB.*
FROM tblA INNER JOIN tblB
ON tblA.pc = tblB.pc AND
tblA.id NOT IN (SELECT tblA.id
FROM tblA INNER JOIN tblB
ON tblA.sub = tblB.sub)
GROUP BY tblA.id
UNION
SELECT tblA.id, tblB.*
FROM tblA INNER JOIN tblB
ON tblA.sub = tblB.sub
GROUP BY tblA.id
) AS tu
ORDER BY id ASC;
See, also, this short demo.
One way of doing it I came up with is to repeat a join condition in where clause:
SELECT *
FROM tblA AS A
INNER JOIN tblB AS B
ON A.pc = B.pc
WHERE A.sub = B.sub
OR A.pc = B.pc