Vertica - Union with Group by is possible? - sql

I have two tables like this :
Nu
i j
1 2
1 3
1 4
Nv
i j
2 1
2 5
3 1
3 6
What I want to do is I need to find :
Select j From Nu UNION Select j from Nv) for every Distinct (Nu.i , Nv.i)
Like this :
Nu.i Nv.i v
1 2 2
1 2 3
1 2 4
1 2 1
1 2 5
1 3 2
1 3 3
1 3 4
1 3 1
1 3 6
Is there any way I can query this with Vertica SQL?
I tried :
Select
Nu.i,
Nv.i,
(Select j from Nu UNION Select j from Nv group by Nv.j) as v
from Nu, Nv;
Error :
ERROR 4840: Subquery used as an expression returned more than one row
And :
Select
Nu.i,
Nv.i,
(Select j from Nu UNION Select j from Nv) as v
from Nu, Nv
group by Nu.i, Nv.i;
Error:
Subqueries in the SELECT or ORDER BY are not supported if the subquery
is not part of the GROUP BY
Please, let me know you suggestion.

Your result table is somehow unexpected to me - I can't figure out the rules on how you would want it to be generated.
Nu has 3 rows. Nv has 4 rows. I was thinking of a CROSS JOIN between the two tables, but that would lead to 3 x 4, that's 12 rows. Like so:
WITH
Nu (i,j) AS (
SELECT 1,2
UNION ALL SELECT 1,3
UNION ALL SELECT 1,4
)
,
Nv(i,j) AS (
SELECT 2,1
UNION ALL SELECT 2,5
UNION ALL SELECT 3,1
UNION ALL SELECT 3,6
)
SELECT
Nu.i AS "Nu.i"
, Nv.i AS "Nv.i"
, Nu.j AS "Nu.j"
, Nv.j AS "Nv.j"
FROM Nu CROSS JOIN Nv;
-- out Nu.i | Nv.i | Nu.j | Nv.j
-- out ------+------+------+------
-- out 1 | 2 | 2 | 1
-- out 1 | 2 | 3 | 1
-- out 1 | 2 | 4 | 1
-- out 1 | 2 | 2 | 5
-- out 1 | 2 | 3 | 5
-- out 1 | 2 | 4 | 5
-- out 1 | 3 | 2 | 1
-- out 1 | 3 | 3 | 1
-- out 1 | 3 | 4 | 1
-- out 1 | 3 | 2 | 6
-- out 1 | 3 | 3 | 6
-- out 1 | 3 | 4 | 6
-- out (12 rows)
-- out
-- out Time: First fetch (12 rows): 14.037 ms. All rows formatted: 14.086 ms
But could it be that, seeing this result table, you can figure out the rest by yourself?

Related

Extract all rows of a column based on the value of another column (SQL query)

I have table
a | b
-----
1 | 3
3 | 2
3 | 4
2 | 5
3 | 6
2 | 7
how to write sql query if a = 1 then result 3 2 4 5 6 7, if a = 3 then 2 4 5 6 7, if 2 then 5 7
here is my query
select *
from table
where a in (select b from table where a = 1) or a = 1
but the result only 3 2 4 6 because 3 has 2 in col b so i want also to have 5 7
thanks
I suspect that you have a hierarchical structure, where a is the parent and b is the child, and that you are looking for all descendents of a given node.
One common way to walk such structure is a hierarchical query. In SQL Server:
with cte as (
select a, b from mytable where a = #your_parameter
union all
select t.a, t.b from mytable t inner join cte c on t.a = c.b
)
select * from cte
Demo on DB Fiddle - when given 3 as parameter:
a | b
-: | -:
3 | 2
3 | 4
3 | 6
2 | 5
2 | 7

Adding a row number respecting the order of each row

I have a table like this
id, period, tag
1 1 A
1 2 A
1 3 B
1 4 A
1 5 A
1 6 A
2 1 A
2 2 B
2 3 B
2 4 B
2 5 B
2 6 A
I would like to add a new column with a ranking, respecting the order of the row given my column 'period' to obtain something like this
id, period, tag rank
1 1 A 1
1 2 A 1
1 3 B 2
1 4 A 3
1 5 A 3
1 6 A 3
2 1 A 1
2 2 B 2
2 3 B 2
2 4 B 2
2 5 B 2
2 6 A 3
What can I do?
I try rank and dense_rank function without any success
And another candidate for CONDITIONAL_CHANGE_EVENT()
less code, and quite effective, too ...!
WITH
input(id,period,tag) AS (
SELECT 1,1,'A'
UNION ALL SELECT 1,2,'A'
UNION ALL SELECT 1,3,'B'
UNION ALL SELECT 1,4,'A'
UNION ALL SELECT 1,5,'A'
UNION ALL SELECT 1,6,'A'
UNION ALL SELECT 2,1,'A'
UNION ALL SELECT 2,2,'B'
UNION ALL SELECT 2,3,'B'
UNION ALL SELECT 2,4,'B'
UNION ALL SELECT 2,5,'B'
UNION ALL SELECT 2,6,'A'
)
SELECT
*
, CONDITIONAL_CHANGE_EVENT(tag) OVER(PARTITION BY id ORDER BY period) + 1 AS rank
FROM input;
-- out id | period | tag | rank
-- out ----+--------+-----+------
-- out 1 | 1 | A | 1
-- out 1 | 2 | A | 1
-- out 1 | 3 | B | 2
-- out 1 | 4 | A | 3
-- out 1 | 5 | A | 3
-- out 1 | 6 | A | 3
-- out 2 | 1 | A | 1
-- out 2 | 2 | B | 2
-- out 2 | 3 | B | 2
-- out 2 | 4 | B | 2
-- out 2 | 5 | B | 2
-- out 2 | 6 | A | 3
-- out (12 rows)
-- out
-- out Time: First fetch (12 rows): 14.823 ms. All rows formatted: 14.874 ms
One method is a cumulative sum based on a lag():
select t.*,
sum(case when prev_tag = tag then 0 else 1 end) over (partition by id order by period) as rank
from (select t.*, lag(tag) over (partition by id order by period) as prev_tag
from t
) t;

How to do it by simple sql or procedure

object_tbl:
objId(primary key) | name
1 | A
2 | B
3 | C
document_tbl:
documentId | sourceId
1 | 2
2 | 2
3 | 1
4 | 3
5 | 3
6 | 3
objToDoc_tbl:
id | objectId | documentId
1 | 1 | 2
2 | 2 | 4
3 | 2 | 6
4 | 1 | 5
5 | 3 | 1
6 | 1 | 2
Inner join of all table
A 2 2
B 4 3
B 6 3
A 5 3
C 1 2
A 2 2
A 2
B 1
C 1
so Answer is 1(As A is only 2)
Question: - How many object are related to documents from multiple sources.
How we can write sql query for this or it can only we solved by procedure
You can join all the tables and then use having to get objects with count > 1.
with t as (
select t.name, count(distinct d.sourceid) as count
from objtodoc_tbl o
join document_tbl d on o.documentid = d.documentid
join object_tbl t on o.objectid = t.name
group by t.name
having count(distinct d.sourceid) > 1
)
select count(t.name) from t;

SQL: Combinations by Type

I have sets organized by type. I want to find all unique combinations of sets, taking one set from each type. So I start with this:
table1:
row_id type set
1 a 1
2 a 2
3 a 3
4 b 4
5 b 5
6 c 6
and want to get this:
table2:
row_id combo_id type set
1 1 a 1
2 1 b 4
3 1 c 6
4 2 a 2
5 2 b 4
6 2 c 6
7 3 a 3
8 3 b 4
9 3 c 6
10 4 a 1
11 4 b 5
12 4 c 6
13 5 a 2
14 5 b 5
15 5 c 6
16 6 a 3
17 6 b 5
18 6 c 6
The first idea might be to use CROSS JOIN and get something like this:
table3:
row_id combo_id a_set b_set c_set
1 1 1 4 6
2 2 2 4 6
3 3 3 4 6
4 4 1 5 6
5 5 2 5 6
6 6 3 5 6
However, my real data has thousands of types with no upper bound on that number, so I think the setup in table2 is necessary.
I see there are many Stack Overflow questions about SQL combinations. However, none that I found addressed sorting by type, let alone an unbounded number of types.
I'm using PLSQL Developer with Oracle 10g. Thanks!
One method to arrive at your table2 representation would be to use an Up-and-Down hierarchical query:
with table1(row_id, type, val) as (
select 1, 'a', 1 from dual union all
select 2, 'a', 2 from dual union all
select 3, 'a', 3 from dual union all
select 4, 'b', 4 from dual union all
select 5, 'b', 5 from dual union all
select 6, 'c', 6 from dual
), t2 as (
select row_id
, type
, DENSE_RANK() OVER (ORDER BY type desc) type_id
, val
from table1
), Up as (
select row_number() over (partition by CONNECT_BY_ISLEAF
order by SYS_CONNECT_BY_PATH(row_id, ',')) combo_id
, SYS_CONNECT_BY_PATH(row_id, ',') path_id
, prior SYS_CONNECT_BY_PATH(row_id, ',') parent_path_id
, t2.*
, CONNECT_BY_ISLEAF leaf
from t2
connect by type_id = prior type_id+1
start with type_id = 1
), Down as (
select row_number() over (order by CONNECT_BY_ROOT combo_id, type) row_id
, CONNECT_BY_ROOT combo_id combo_id
, type
, val
from Up
connect by path_id = prior parent_path_id
start with leaf = 1
)
select * from Down;
In this solution subquery t2 adds a sequential id for each unique type I've ordered them in reverse order so that the first traversal will end up with type 'a' as the leaf nodes.
In the first tree traversal (subquery Up) I add a unique combo code to all the leaf records for grouping purposes and add the path_id and parent_path_id columns used in the next tree traversal. This is also the stage where all the new rows are generated.
In the second tree traversal (subquery Down) I start at the leaf nodes from the Up traversal and climb back down to the root keeping the root (leaf?) combo_id generated in the prior traversal. No additional rows are generated in this stage since it's a straight shot from the leaf back to the root of the tree.
The final result:
ROW_ID COMBO_ID T VAL
-------- ---------- - ----------
1 1 a 1
2 1 b 4
3 1 c 6
4 2 a 2
5 2 b 4
6 2 c 6
7 3 a 3
8 3 b 4
9 3 c 6
10 4 a 1
11 4 b 5
12 4 c 6
13 5 a 2
14 5 b 5
15 5 c 6
16 6 a 3
17 6 b 5
18 6 c 6
18 rows selected
If you want your Table3 representation you can change the select * from Down to this:
select row_number() over (order by combo_id) row_id
, pvt.*
from (select combo_id, type, val from Down)
pivot (max(val) "SET"
for (type) in ('a' A
,'b' B
,'c' C)) pvt;
Yielding the following result:
ROW_ID COMBO_ID A_SET B_SET C_SET
---------- ---------- ---------- ---------- ----------
1 1 1 4 6
2 2 2 4 6
3 3 3 4 6
4 4 1 5 6
5 5 2 5 6
6 6 3 5 6
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table1 ( "rowid", "type", "set" ) AS
SELECT 1, 'a', 1 FROM DUAL
UNION ALL SELECT 2, 'a', 2 FROM DUAL
UNION ALL SELECT 3, 'a', 3 FROM DUAL
UNION ALL SELECT 4, 'b', 4 FROM DUAL
UNION ALL SELECT 5, 'b', 5 FROM DUAL
UNION ALL SELECT 6, 'c', 6 FROM DUAL
//
CREATE TYPE combo_sets AS OBJECT(
"type" CHAR(1),
idx NUMBER(5,0),
sets SYS.ODCINUMBERLIST
);
//
CREATE TYPE t_combo_sets AS TABLE OF combo_sets;
//
CREATE TYPE combo AS OBJECT(
"rowid" NUMBER(8,0),
"comboid" NUMBER(8,0),
"type" CHAR(1),
"set" NUMBER(5,0)
);
//
CREATE TYPE t_combos AS TABLE of combo;
//
CREATE OR REPLACE FUNCTION get_combos
RETURN t_combos PIPELINED
AS
v_combo_sets t_combo_sets;
i NUMBER(5,0);
r NUMBER(5,0) := 1;
c NUMBER(5,0) := 1;
BEGIN
SELECT combo_sets(
"type",
1,
CAST( COLLECT( "set" ORDER BY "set" ) AS SYS.ODCINUMBERLIST )
)
BULK COLLECT INTO v_combo_sets
FROM table1
GROUP BY "type";
i := 1;
WHILE i <= v_combo_sets.COUNT LOOP
FOR j IN 1 .. v_combo_sets.COUNT LOOP
PIPE ROW(
combo(
r,
c,
v_combo_sets(j)."type",
v_combo_sets(j).sets( v_combo_sets(j).idx )
)
);
r := r + 1;
END LOOP;
c := c + 1;
i := 1;
WHILE i <= v_combo_sets.COUNT AND v_combo_sets(i).idx = v_combo_sets(i).sets.COUNT LOOP
v_combo_sets(i).idx := 1;
i := i + 1;
END LOOP;
IF i <= v_combo_sets.COUNT THEN
v_combo_sets(i).idx := v_combo_sets(i).idx + 1;
END IF;
END LOOP;
RETURN;
END;
//
Query 1:
SELECT *
FROM TABLE( get_combos )
Results:
| rowid | comboid | type | set |
|-------|---------|------|-----|
| 1 | 1 | a | 1 |
| 2 | 1 | b | 4 |
| 3 | 1 | c | 6 |
| 4 | 2 | a | 2 |
| 5 | 2 | b | 4 |
| 6 | 2 | c | 6 |
| 7 | 3 | a | 3 |
| 8 | 3 | b | 4 |
| 9 | 3 | c | 6 |
| 10 | 4 | a | 1 |
| 11 | 4 | b | 5 |
| 12 | 4 | c | 6 |
| 13 | 5 | a | 2 |
| 14 | 5 | b | 5 |
| 15 | 5 | c | 6 |
| 16 | 6 | a | 3 |
| 17 | 6 | b | 5 |
| 18 | 6 | c | 6 |

Select non distinct rows from two columns

My question is very similar to Multiple NOT distinct only it deals with multiple columns instead of one. I have a table like so:
A B C
1 1 0
1 2 1
2 1 2
2 1 3
2 2 4
2 3 5
2 3 6
3 1 7
3 3 8
3 1 9
And the result should be:
A B C
2 1 2
2 1 3
2 3 5
2 3 6
3 1 7
3 1 9
Essentially, like the above question, removing all unique entries only where uniqueness is determined by two columns instead of one. I already tried various tweaks to the above answer but couldn't get any of them to work.
You are using SQL Server, so this is easier than in Access:
select A, B, C
from (select t.*, count(*) over (partition by A, B) as cnt
from t
) t
where cnt > 1;
This use of count(*) is as a window function. It is counting the number of rows with the same value of A and B. The final where just selects the rows that have more than one entry.
Another possible solution with EXISTS
SELECT a, b, c
FROM Table1 t
WHERE EXISTS
(
SELECT 1
FROM Table1
WHERE a = t.a
AND b = t.b
AND c <> t.c
)
It should be fast enough.
Output:
| A | B | C |
-------------
| 2 | 1 | 2 |
| 2 | 1 | 3 |
| 2 | 3 | 5 |
| 2 | 3 | 6 |
| 3 | 1 | 7 |
| 3 | 1 | 9 |
Here is SQLFiddle demo