How to multiply rows in oracle query by count column? - sql

I have example table:
OVERALL_COUNT | NAME | OTHER_ATTRIBUTE
1 | ABC | q
5 | ACB | w
2 | CBA | e
........
[many rows, like thousands]
Update: the rows in overall_count column are unknown, they are calculated by other query
I need to get specific result where names and other attributes duplicates by overall_count.
How I can write query to get result like this?
ABC | q
ACB | w
ACB | w
ACB | w
ACB | w
ACB | w
CBA | e
CBA | e

For 12c and above you may use lateral join:
with a(OVERALL_COUNT, NAME, OTHER_ATTRIBUTE) as (
select 1, 'ABC', 'q' from dual union all
select 5, 'ACB', 'w' from dual union all
select 2, 'CBA', 'e' from dual
)
select a.*
from a
cross join lateral (
select 1
from dual
connect by level <= a.overall_count
)
order by name
OVERALL_COUNT | NAME | OTHER_ATTRIBUTE
------------: | :--- | :--------------
1 | ABC | q
5 | ACB | w
5 | ACB | w
5 | ACB | w
5 | ACB | w
5 | ACB | w
2 | CBA | e
2 | CBA | e
Or for 11 version:
with b(
overall_count
, name
, other_attribute
, l
) as (
select a.*, 1
from a
union all
select
overall_count
, name
, other_attribute
, l + 1
from b
where l < overall_count
)
select *
from b
order by name
OVERALL_COUNT | NAME | OTHER_ATTRIBUTE | L
------------: | :--- | :-------------- | -:
1 | ABC | q | 1
5 | ACB | w | 1
5 | ACB | w | 2
5 | ACB | w | 4
5 | ACB | w | 5
5 | ACB | w | 3
2 | CBA | e | 2
2 | CBA | e | 1
db<>fiddle here

You could use a join approach with the help of a sequence table:
WITH seq AS (
SELECT 1 AS cnt FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT 3 FROM dual UNION ALL
SELECT 4 FROM dual UNION ALL
SELECT 5 FROM dual
)
SELECT t1.NAME, t1.OTHER_ATTRIBUTE
FROM yourTable t1
INNER JOIN seq t2
ON t2.cnt <= t1.OVERALL_COUNT
ORDER BY t1.NAME;
Demo
Note that I hard-coded a trivial sequence table with only 5 values (which happens to cover all your sample data). Check this SO question and answers for some more ideas on how to work with sequence tables in Oracle.

Related

Filtering a table via another table's values

I have 2 tables:
Value
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
| 2 | Jane |
| 3 | Joe |
+----+-------+
Filter
+----+---------+------+
| id | valueid | type |
+----+---------+------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 1 | D |
| 5 | 2 | A |
| 6 | 2 | C |
| 7 | 2 | E |
| 8 | 3 | A |
| 9 | 3 | D |
+----+---------+------+
I need to retrieve the values from the Value table where the related Filter table does not contain the type 'B' or 'C'
So in this quick example this would be only Joe.
Please note this is a DB2 DB and i have limited permissions to run selects only.
Or also a NOT IN (<*fullselect*) predicate:
Only that my result is 'Joe', not 'Jane' - and the data constellation would point to that ...
WITH
-- your input, sans reserved words
val(id,nam) AS (
SELECT 1,'Peter' FROM sysibm.sysdummy1
UNION ALL SELECT 2,'Jane' FROM sysibm.sysdummy1
UNION ALL SELECT 3,'Joe' FROM sysibm.sysdummy1
)
,
filtr(id,valueid,typ) AS (
SELECT 1,1,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 2,1,'B' FROM sysibm.sysdummy1
UNION ALL SELECT 3,1,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 4,1,'D' FROM sysibm.sysdummy1
UNION ALL SELECT 5,2,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 6,2,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 7,2,'E' FROM sysibm.sysdummy1
UNION ALL SELECT 8,3,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 9,3,'D' FROM sysibm.sysdummy1
)
-- real query starts here
SELECT
*
FROM val
WHERE id NOT IN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
)
;
-- out id | nam
-- out ----+-------
-- out 3 | Joe
Or also, a failing left join:
SELECT
val.*
FROM val
LEFT JOIN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
) filtr
ON filtr.valueid = val.id
WHERE valueid IS NULL
You can use EXISTS, as in:
select *
from value v
where not exists (
select null from filter f
where f.valueid = v.id and f.type in ('B', 'C')
);
Result:
ID NAME
--- -----
3 Joe
See running example at db<>fiddle.

Postgresql hierarchical (tree) query

I found few topics about it but none fits my expected results.
I have levels of categories stored in the table, just want to display it as tree structure.
All answers are kind of following query:
DB FIDDLE
WITH RECURSIVE cte AS (
SELECT category_id, category_name, parent_category, 1 AS level
FROM category
WHERE level = 1
UNION ALL
SELECT c.category_id, c.category_name, c.parent_category, ct.level + 1
FROM cte ct
JOIN category c ON c.parent_category = ct.category_id
)
SELECT *
FROM cte;
But the results are like
level
1
1
2
2
2
3
3
3
3
3
What I want to achieve is
level
1
2
3
3
2
3
3
1
2
3
3
2
3
3
You would typically keek track of the path to each node and use that for ordering. In Postgres, arrays come handy for this:
with recursive cte as (
select category_id, category_name, parent_category, 1 as level, array[category_id] path
from category
where parent_category is null
union all
select c.category_id, c.category_name, c.parent_category, ct.level + 1, ct.path || c.category_id
from cte ct
join category c on c.parent_category = ct.category_id
)
select *
from cte
order by path
Note that there is no need to store the level in the table; you can compute the information on the fly as you iterate. To identify the root nodes, you can filter on rows whose parent is null.
In your db fiddle, the query returns:
category_id | category_name | parent_category | level | path
----------: | :------------ | --------------: | ----: | :-------
1 | cat1 | null | 1 | {1}
3 | cat3 | 1 | 2 | {1,3}
8 | cat8 | 3 | 3 | {1,3,8}
9 | cat9 | 3 | 3 | {1,3,9}
4 | cat4 | 1 | 2 | {1,4}
6 | cat6 | 4 | 3 | {1,4,6}
7 | cat7 | 4 | 3 | {1,4,7}
5 | cat5 | 1 | 2 | {1,5}
10 | cat10 | 5 | 3 | {1,5,10}
11 | cat11 | 5 | 3 | {1,5,11}
2 | cat2 | null | 1 | {2}
You can keep track of the hierarchy as an array and use that for ordering:
WITH RECURSIVE cte AS (
SELECT category_id, category_name, parent_category, 1 AS level, array[category_id] as categories
FROM category
WHERE level = 1
UNION ALL
SELECT c.category_id, c.category_name, c.parent_category, ct.level + 1, ct.categories || c.category_id
FROM cte ct JOIN
category c
ON c.parent_category = ct.category_id
)
SELECT *
FROM cte
ORDER BY categories;
Here is a db<>fiddle.

Values Disappear when Filtering Correlated Subquery

This question is related to the recent answer I provided here.
Setup
Using MS Access 2007.
Assume I have a table called mytable consisting of three fields:
id Long Integer AutoNumber (PK)
type Text
num Long Integer
With the following sample data:
+----+------+-----+
| id | type | num |
+----+------+-----+
| 1 | A | 10 |
| 2 | A | 20 |
| 3 | A | 30 |
| 4 | B | 40 |
| 5 | B | 50 |
| 6 | B | 60 |
| 7 | C | 70 |
| 8 | C | 80 |
| 9 | C | 90 |
| 10 | D | 100 |
+----+------+-----+
Similar to the linked answer, say I wish to output the three fields, with a running total for each type value, with the value of the running total limited to a maximum of 100, I might use a correlated subquery such as the following:
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
where q.rt < 100
This produces the expected result:
+----+------+-----+----+
| id | type | num | rt |
+----+------+-----+----+
| 1 | A | 10 | 10 |
| 2 | A | 20 | 30 |
| 3 | A | 30 | 60 |
| 4 | B | 40 | 40 |
| 5 | B | 50 | 90 |
| 7 | C | 70 | 70 |
+----+------+-----+----+
Observation
Now assume that I wish to filter the result to show only those values for type like "[AB]".
If I use either of the following queries:
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
where t.type like "[AB]"
) q
where q.rt < 100
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
where q.rt < 100 and q.type like "[AB]"
The results are filtered as expected, but the values in the rt (running total) column disappear:
+----+------+-----+----+
| id | type | num | rt |
+----+------+-----+----+
| 1 | A | 10 | |
| 2 | A | 20 | |
| 3 | A | 30 | |
| 4 | B | 40 | |
| 5 | B | 50 | |
+----+------+-----+----+
Question
Why would the filter cause the values returned by the correlated subquery to disappear?
Thank you for your time reading my question and in advance for any advice you can offer.
Moving type criteria to the aggregate subquery works.
One less tier works but the aggregate subquery has to repeat in WHERE clause:
SELECT mytable.*, (select sum(u.num)
from mytable u where u.type = MyTable.type and u.id <= MyTable.id
) AS rt
FROM mytable
WHERE ((((select sum(u.num)
from mytable u where u.type = MyTable.type and u.id <= MyTable.id
))<100) AND ((mytable.[type]) Like "[AB]"));
An INNER JOIN version:
select MyTable.*, q.* from MyTable INNER JOIN
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
ON q.id=MyTable.ID
where q.rt < 100 AND MyTable.Type LIKE "[AB]";

Efficient query to Group by column name in SQL or hive

Imagine I have a table with 2 columns m_1 and m_2:
m1 | m2
3 | 17
3 | 18
4 | 17
9 | 9
I would like to get a table with 3 columns:
m is the index of m (in my exemple 1 or 2)
d is the data contains in the table .
count is the number of occurence of each data, group by value and index.
In the example, the result is:
m | d | count
m_1 | 3 | 2
m_1 | 4 | 1
m_1 | 9 | 1
m_2 | 17| 2
m_2 | 18| 1
m_2 | 9 | 1
The first ligne mus be read as 'data 3 occurs 2 times in column m_1'?
A naive solution is to execute two times a parametric query like this:
for (i in 1 .. 2)
SELECT CONCAT('m_', i), m_i, count(*) FROM table GROUP BY m_i
But this algorithm scans my table two times. This is a problem since I have 255 columns m and bilion of rows.
Will the solution becomes easier if I use hive instead of a relational data base?
You can write this using union all and group by:
select colname, d, count(*)
from ((select 'm_1' as colname, m1 as d from t) union all
(select 'm_2' as colname, m2 as d from t)
) m12
group by colname, d;
posexplode(array(m1,m2))
select concat('m_',cast(pe.pos+1 as string)) as m
,pe.val as d
,count(*) as `count`
from mytable t
lateral view posexplode(array(m1,m2)) pe
group by pos
,val
;
+------+-----+--------+
| m | d | count |
+------+-----+--------+
| m_1 | 3 | 2 |
| m_1 | 4 | 1 |
| m_1 | 9 | 1 |
| m_2 | 9 | 1 |
| m_2 | 17 | 2 |
| m_2 | 18 | 1 |
+------+-----+--------+

order by after full outer join

I create the following table on http://sqlfiddle.com in PostgreSQL 9.3.1 mode:
CREATE TABLE t
(
id serial primary key,
m varchar(1),
d varchar(1),
c int
);
INSERT INTO t
(m, d, c)
VALUES
('A', '1', 101),
('A', '2', 102),
('A', '3', 103),
('B', '1', 104),
('B', '3', 105);
table:
| ID | M | D | C |
|----|---|---|-----|
| 1 | A | 1 | 101 |
| 2 | A | 2 | 102 |
| 3 | A | 3 | 103 |
| 4 | B | 1 | 104 |
| 5 | B | 3 | 105 |
From this I want to generate such a table:
| M | D | ID | C |
|---|---|--------|--------|
| A | 1 | 1 | 101 |
| A | 2 | 2 | 102 |
| A | 3 | 3 | 103 |
| B | 1 | 4 | 104 |
| B | 2 | (null) | (null) |
| B | 3 | 5 | 105 |
but with my current statement
select * from
(select * from
(select distinct m from t) as dummy1,
(select distinct d from t) as dummy2) as combi
full outer join
t
on combi.d = t.d and combi.m = t.m
I only get the following
| M | D | ID | C |
|---|---|--------|--------|
| A | 1 | 1 | 101 |
| B | 1 | 4 | 104 |
| A | 2 | 2 | 102 |
| A | 3 | 3 | 103 |
| B | 3 | 5 | 105 |
| B | 2 | (null) | (null) |
Attempts to order it by m,d fail so far:
select * from
(select * from
(select * from
(select * from
(select distinct m from t) as dummy1,
(select distinct d from t) as dummy2) as kombi
full outer join
t
on kombi.d = t.d and kombi.m = t.m) as result)
order by result.m
Error message:
ERROR: subquery in FROM must have an alias: select * from (select * from (select * from (select * from (select distinct m from t) as dummy1, (select distinct d from t) as dummy2) as kombi full outer join t on kombi.d = t.d and kombi.m = t.m) as result) order by result.m
It would be cool if somebody could point out to me what I am doing wrong and perhaps show the correct statement.
select * from
(select kombi.m, kombi.d, t.id, t.c from
(select * from
(select distinct m from t) as dummy1,
(select distinct d from t) as dummy2) as kombi
full outer join t
on kombi.d = t.d and kombi.m = t.m) as result
order by result.m, result.d
I think your problem is the order. You can solve this problem with the order by clause:
select * from
(select * from
(select distinct m from t) as dummy1,
(select distinct d from t) as dummy2) as combi
full outer join
t
on combi.d = t.d and combi.m = t.m
order by combi.m, combi.d
You need to specify which data you would like to order. In this case you get back the row from the combi table, so you need to say that.
http://sqlfiddle.com/#!15/ddc0e/17
You could also use column numbers instead of names to do the ordering.
select * from
(select * from
(select distinct m from t) as dummy1,
(select distinct d from t) as dummy2) as combi
full outer join
t
on combi.d = t.d and combi.m = t.m
order by 1,2;
| M | D | ID | C |
|---|---|--------|--------|
| A | 1 | 1 | 101 |
| A | 2 | 2 | 102 |
| A | 3 | 3 | 103 |
| B | 1 | 4 | 104 |
| B | 2 | (null) | (null) |
| B | 3 | 5 | 105 |
you just need a pivot table
the query is very simple
select classes.M, p.i as D, t.ID, t.C
from (select M, max(D) MaxValue from t group by m) classes
inner join pivot p
on p.i =< classes.MaxValue
left join t
on t.M = classes.M
and t.D = p.i
pivot table is a dummy table some how
CREATE TABLE Pivot (
i INT,
PRIMARY KEY(i)
)
populate is some how
CREATE TABLE Foo(
i CHAR(1)
)
INSERT INTO Foo VALUES('0')
INSERT INTO Foo VALUES('1')
INSERT INTO Foo VALUES('2')
INSERT INTO Foo VALUES('3')
INSERT INTO Foo VALUES('4')
INSERT INTO Foo VALUES('5')
INSERT INTO Foo VALUES('6')
INSERT INTO Foo VALUES('7')
INSERT INTO Foo VALUES('8')
INSERT INTO Foo VALUES('9')
Using the 10 rows in the Foo table, you can easily populate the Pivot table with 1,000 rows. To get 1,000 rows from 10 rows, join Foo to itself three times to create a Cartesian product:
INSERT INTO Pivot
SELECT f1.i+f2.i+f3.i
FROM Foo f1, Foo F2, Foo f3
you can read about that in Transac-SQL Cookbook by Jonathan Gennick, Ales Spetic
You just need to order by the final column definitions. t.m and t.d. SO your final SQL would be...
SELECT *
FROM (SELECT *
FROM (SELECT DISTINCT m FROM t) AS dummy1,
(SELECT DISTINCT d FROM t) AS dummy2) AS combi
FULL OUTER JOIN t
ON combi.d = t.d
AND combi.m = t.m
ORDER BY t.m,
t.d;
Also for query optimization perspective, it is better to now have many layers of sub queries.
I think you need another correlation name - dummy3? - after 'as result )' before the order by.