Get one row per unique column value combination (`DISTINCT ON` operation without using it) - sql

I have a table with 5 columns, For each unique combination of the first three columns, I want a single sample row. I don't care which values are considered for columns 4 and 5, as long as they come from the same row (they should be aligned).
I realise I can do a DISTINCT to fetch on the first three columns to fetch unique combinations of the first three columns. But the problems is then I cannot get 4th and 5th column values.
I considered GROUP BY, I can do a group by on the first three columns, then I can do a MIN(col4), MIN(col5). But there is no guarantee that values of col4 and col5 are aligned (from the same row).
The DB I am using does not support DISTINCT ON operation, which I realise is what I really need.
How do I perform what DISTINCT ON does without actually using that operation ?
I am guessing this is how I would write the SQL if DISTINCT ON was supported:
SELECT
DISTINCT ON (col1, col2, col3)
col1, col2, col3, col4, col5
FROM TABLE_NAME

select
col1, col2, col3, col4, col5
from (
select col1, col2, col3, col4, col5,
row_number() over (partition by col1, col2, col3) as n
from table_name
)
where n = 1

Related

Oracle SQL: How to convert one column of Select to rows

I am new to Oracle and am looking for a way to convert 1 column in a Select to rows.
My first approach was using Listagg which does exactly what I want but the character limit for this is not enough for my case.
As an alternative I would like to do the following.
SELECT
t.col1
, t.col2
, t.col3
, t.col4
, t.col5
FROM
my table t
Instead of the standard output of t.col1 t.col2 t.col3 t.col4 t.col5 I would like t.col2 to appear in rows (i.e. below each other) instead of in columns (next to each other). Col2 always contains a value and each of them should appear in a separate row.
When searching for a solution to this I came across Unpivot and Decode but am not sure if and how this could be applied here.
Can someone tell me how this can be achieved ?
Many thanks in advance for any help,
Mike
A simple method -- if your data is not too large -- is just to use union all. Your description makes it sound like you want this:
select col1, col2, col5
from t
where col2 is not null
union all
select col1, col3, col5
from t
where col2 is not null
union all
select col1, col4, col5
from t
where col2 is not null;
Hmmm, or if you just want the distinct values in col2:
select distinct col2
from t;
You are looking for the UNPIVOT function
SELECT col
FROM my table t
UNPIVOT INCLUDE NULLS (col FOR source_column_name IN (col1, col2, col3, col4, col5)
COL
----
Value 1
Value 2
Value 3
Value 4
Value 5
The result contains five rows with one column COL each that contains the value from the columns COL1 to COL5. The (unused) column SOURCE_COLUMN_NAME contains the name of the column where the data is coming from. You can remove the INCLUDING NULLS if you are only interested in rows the COL IS NOT NULL.
See the ORACLE-BASE article on PIVOT and UNPIVOT operators for more details.

Loop variable SQL query to append tables

I have a database that has 100+ tables, all with the same header. I want to merge these tables into one. Also within the database is a table that lists all the other tables (an inventory of the database per se).
I'm looking for a way to loop the following SQL append query so VaryingTableName changes to follow through my inventory table:
INSERT INTO MainTable IN 'C:\newDBFile.accdb'
SELECT VaryingTableName.*
FROM VaryingTableName;
If there were a way to do this without the inventory table, that's fine too.
It's not the most pretty solution and involves no automation but you could do:
INSERT INTO MainTable (Col1, Col2, Col3, Col4) IN 'C:\newDBFile.accdb'
SELECT Col1, Col2, Col3, Col4
FROM (
SELECT Col1, Col2, Col3, Col4
FROM OldTable1
UNION ALL
SELECT Col1, Col2, Col3, Col4
FROM OldTable2
...)

De-duplicating rows in a table with respect to certain columns and retaining the corresponding values in the other columns in HIVE

I need to create a temporary table in HIVE using an existing table that has 7 columns. I just want to get rid of duplicates with respect to first three columns and also retain the corresponding values in the other 4 columns. I don't care which row is actually dropped while de-duplicating using first three rows alone.
You could use something as below if you are not considered about ordering
create table table2 as
select col1, col2, col3,
,split(agg_col,"|")[0] as col4
,split(agg_col,"|")[1] as col5
,split(agg_col,"|")[2] as col6
,split(agg_col,"|")[3] as col7
from (Select col1, col2, col3,
max(concat(cast(col4 as string),"|",
cast(col5 as string),"|",
cast(col6 as string),"|",
cast(col7 as string))) as agg_col
from table1
group by col1,col2,col3 ) A;
Below is another approach, which gives much control over ordering but slower than above approach
create table table2 as
select col1, col2, col3,max(col4), max(col5), max(col6), max(col7)
from (Select col1, col2, col3,col4, col5, col6, col7,
rank() over ( partition by col1, col2, col3
order by col4 desc, col5 desc, col6 desc, col7 desc ) as col_rank
from table1 ) A
where A.col_rank = 1
GROUP BY col1, col2, col3;
rank() over(..) function returns more than one column with rank as '1' if order by columns are all equal. In our case if there are 2 columns with exact same values for all seven columns then there will be duplicates when we use filter as col_rank =1. These duplicates can be eleminated using max and group by clauses as written in above query.

PL/SQL Inserting into a table minus another table but inserting another column at the same time

I have a small problem, say I have a table with 5 columns, I insert into that table using MINUS from another table that has 4 columns.
I am having problem inserting a (null) or ' ' value into the 5th column when I do that statement as it says:
invalid number of columns selected (table I am minusing from does not have the 5th column)
This is my code I use for the insert into statement
INSERT INTO my_table(SELECT col1, col2, col3, col4 FROM that_table
MINUS SELECT col1, col2, col3, col4 FROM my_table);
This code works, it copies whatever I need if I don't create a 5th column, is there a way to insert into my table values from the other table alongside a 5th column in my_table?
Thank you
Assuming I'm understanding your question, you want to select all records from that_table and insert those into my_table where the first 4 columns don't exist, using null as the value for the 5th column? If so, you can use not exists:
insert into my_table (col1, col2, col3, col4, col5)
select col1, col2, col3, col4, null
from that_table tt
where not exists (
select 1
from my_table mt
where tt.col1 = mt.col1 and tt.col2 = mt.col2
and tt.col3 = mt.col3 and tt.col4 = mt.col4
)
You should also be able to use Minus, just make sure you have the same number of columns and same data types:
insert into my_table (col1, col2, col3, col4, col5)
select col1, col2, col3, col4, null
from that_table
minus
select col1, col2, col3, col4, null
from my_table
If you want to insert only the selected columns, then try this:
insert into my_table (col1, col2, col3, col4, col5) select col1, col2, col3, col4, null from that_table;
You can find more information at http://www.w3schools.com/sql/sql_insert_into_select.asp

Have unpivot automatically grab column list (oracle 11g)

This is a follow up question to Transpose one row into many rows Oracle
I want to be able to unpivot an arbitrary query result.
To unpivot a table manually, I would do:
select value_type, value from (
(
-- query to be unpivoted
-- EG: select col1, col2, col3, col4, col5 from table
)
unpivot
(
-- Line I would like to change
value for value_type in (col1, col2, col3, col4, col5)
)
);
This works for all queries that return 5 columns, called col1, col2, etc. Is there something I put in instead of value for value_type in (col1, col2, col3, col4, col5) that will grab all the column names from the query/view/table that is selected in the first part?
You could create a stored procedure to do this in PL/SQL by dynamically creating your SQL statement as a string an then using execute immediate to execute it and return a cursor.