reducing oracle table entity - sql

I have a table like this:
I want a query that make my table like this:

You can use aggregation:
select max(id1) as id1, max(id2) as id2, max(id3) as id3
from . . .

I would prefer to use SUM() here, because you might have negative numbers in a given column:
select sum(id1) as id1, sum(id2) as id2, sum(id3) as id3
from yourTable
If we use MAX(), then it would fail, if for example there were a -2 in one of the columns. In that case, the max would return zero.

I'm not sure that solution is optimal for executing, but:
select (select id1 from table where id1 <> 0) id1,
(select id2 from table where id2 <> 0) id2,
(select id3 from table where id3 <> 0) id3
from dual;

Related

Loop in the same table on ids found with an id

I have a simple table:
Main_ID
Sub_ID1
Sub_ID2
ID1
ID2
ID3
ID2
ID4
ID5
ID3
ID7
ID12
Where a product in Main_ID is made with products in Sub_ID1 and Sub_ID2.
I would like, for a given id, have all the products (sub_ids) necessary for its realisation.
For example: For the id ID1, I will have ID2, ID3, ID4, ID5, ID7, ID12.
(ID1 is made with ID2 and ID3, but ID2 is made with ID4 and ID5, and ID3 is made with ID7 and ID12, etc.)
I've tried some left join, but I miss something I guess.
SELECT t1.Main_ID
FROM my_table t1
INNER JOIN my_table t2 ON t2.Sub_ID1 t1.Main_ID OR t2.Sub_ID2 t1.Main_ID
WHERE t1.Main_ID LIKE 'ID1'
You can do this in a reliable way using a recursive query.
Although you need to do a preprocessing step first: "Sub_ID1" and "Sub_ID2" should be found within one single Sub_ID column, using the UNION ALL operator. We're calling the output of this step as "crafted table".
Then you can apply recursion defining these two steps:
base step, select the "main_id" record you want to inspect
recursive step, joining base-step table with the crafted table on matching <"Main_ID", "SubID">
WITH RECURSIVE one_subid AS (
SELECT Main_ID, Sub_ID1 AS Sub_ID FROM tab
UNION ALL
SELECT Main_ID, Sub_ID2 AS Sub_ID FROM tab
), cte_rec AS (
SELECT Main_ID, Sub_ID FROM one_subid WHERE Main_ID = 'ID1'
UNION ALL
SELECT t2.Sub_ID, t1.Sub_ID
FROM one_subid t1
INNER JOIN cte_rec t2 ON t1.Main_ID = t2.Sub_ID
)
SELECT Sub_ID FROM cte_rec
Output for 'Main_ID = ID1':
sub_id
ID2
ID3
ID4
ID7
ID5
ID12
Check the demo here.

SQL: exclude rows that are duplicates according to some column arrangement

In my problem, I have these rows e.g.:
id1 |name1| id2 | name2| c
1 10994,Apple,22265,Banana,103
2 22265,Banana,10994,Apple,103
3 20945,Coconut,20391,Date,101
4 20391,Date,20945,Coconut,101
They show pair-wise combinations of ids and names, together with another column c.
I consider row 1+2, and 3+4 as duplicates, considering the pairings of ids and names. Rows 1+2, or 3+4 show basically the same information.
I had no luck removing the duplicates with grouping, because id1 + id2, or name1 + name2 respectively are distinct columns.
Can I add something to my SQL query that removes these 'duplicates', so that only rows 1+3 are output?
One approach here would to be aggregate by the c column and then use a least/greatest trick to tease apart the duplicates. Here is a general solution which should work on any version of SQL:
SELECT DISTINCT c, CASE WHEN id1 < id2 THEN id1 ELSE id2 END AS id1,
CASE WHEN id1 < id2 THEN id2 ELSE id1 END AS id2,
CASE WHEN name1 < name2 THEN name1 ELSE name2 END AS name1,
CASE WHEN name1 < name2 THEN name2 ELSE name1 END AS name2
FROM yourTable;
Here is a working demo.
Note that on databases which have a LEAST and GREATEST function, the above can be simplified to:
SELECT DISTINCT c, LEAST(id1, id2) AS id1,
GREATEST(id1, id2) AS id2,
LEAST(name1, name2) AS name1,
GREATEST(name1, name2) AS name2
FROM yourTable;
You could try using not exists. You have not provided any existing query but something like:
select *
from Tablename t
where not exists (
select * from Tablename t2
where t2.id2 = t.id1
);

How can I select a different column from each row?

I have a table which has three columns with three records. How can I select first value of the first column, second value of the second column, third value of the third column?
table
============
test_tab
id1 id2 id2
=== === ====
100 400 700
200 500 800
300 600 900
Required output like :
100 500 900
How can I achieve this by using Oracle SQL or PL/SQL?
First of all, How you would identify which row is the first and which one is second?
Oracle does not guarantee the order of the records, it must be ordered using order by clause explicitly else it will be, we can say random order (The default query output)
With your test data and result, You can use the following query:
Note: I am considering the third column as ID3 and ordering the rows using ID1
SELECT
MAX(CASE RN
WHEN 1 THEN ID1
END) AS ID1,
MAX(CASE RN
WHEN 2 THEN ID2
END) AS ID2,
MAX(CASE RN
WHEN 3 THEN ID3
END) AS ID3
FROM
(
SELECT
ID1,
ID2,
ID3,
ROW_NUMBER() OVER(
ORDER BY
ID1
) RN
FROM
TEST_TAB
);
Cheers!!
You will need a means of establishing what "first" means.
In Oracle, here is one way to address the example from your question:
create table test_tab(
id1 integer,
id2 integer,
id3 integer
);
insert into test_tab values(100,400,700);
insert into test_tab values(200,500,800);
insert into test_tab values(300,600,900);
commit;
select sum(decode(pos, 1, id1, null)) id1,
sum(decode(pos, 2, id2, null)) id2,
sum(decode(pos, 3, id3, null)) id3
from(
-- this subquery produces a rank column for the whole dataset
-- with an explicit order
select id1, id2, id3, rank() over (order by id1, id2, id3) pos from TEST_TAB
);
In this implementation, the subquery is used to establish an ordering of the rows, adding a new pos column based on the rank() function.
The sum(decode(pos, 3, id3, null)) construct is an Oracle idiom for picking one specific row (row 3 in this case) while ignoring the others.
Basically, for your three rows, the decode will result in NULL for any row but the one with the specified number, so the expression for id3 will only have a non-null value for the third row, hence the sum over the group will be equal to id3 in row 3.
There are many ways to do it, this is just one, and you will likely need to make some adjustments to this implementation for it to work properly in your real code.

Return most recent data

I have a database table, which is structured like this:
CREATE table match (ID1 int, ID2 int, CreatedDate, ConnectDisconnect)
I am trying to write an SQL statement that will return the most recent rematch record grouped by id1 and id2. For example, please see the data below:
1,2,'2014-06-05', C
1,3,'2014-06-05', C
1,4,'2014-06-05', C
N1,N2,'2014-06-05',D
Please see the SQL statement below:
select max(CreatedDate), ID1,ID2 FROM match
group by ID1,ID2
This will show the most recent decision that was made on ID1 and ID2. The problem is that the matching records can be either way around. For example:
1,2,'2014-06-04', C
2,1,'2014-06-05', D
The data above shows that records 1 and 2 were connected on 04/06/2014 and disconnected on 05/06/2014. My query above will return two rows, however I only want it to return one row i.e. the most recent (the data dated 05/06/14 in the case above).
Here is one approach, using case and row_number():
select m.*
from (select m.*,
row_number() over (partition by (case when id1 < id2 then id1 else id2 end),
(case when id1 < id2 then id2 else id1 end)
order by CreatedDate desc
) as seqnum
from match m
) m
where seqnum = 1;
It's ugly but this may do what you want (not sure what the syntax of concatenation is in SQL Server):
select max(CreatedDate), case when ID1 < ID2 concat(ID1,',',ID2) else concat(ID2,',',ID1) end as combinedIds
FROM match
group by case when ID1 < ID2 concat(ID1,',',ID2) else concat(ID2,',',ID1) end

Insert into from existing table?

I know there's probably a simple way to do this, but I can't seem to remember how.
Basically, I have a table with 2 fields, ID1 and ID2. I want to find all fields with an id2 = 1, and insert another record into the table with that id1 and a different id2.
So the query to select all the fields that I want a new record for is:
select id1 from mytable where id2 = 1
And then for each of those I want to insert:
insert into mytable(id1, id2) values([this id1], 6)
You basically just have to combine the two queries you've already written in a slightly different way:
INSERT INTO mytable (id1, id2)
SELECT id1, 6 FROM mytable WHERE id2 = 1
Would this work for your purpose?
INSERT INTO mytable(id1, id2)
SELECT id, 6
FROM mytable
WHERE id2 = 1
Ok, I think I got it. Just had the syntax wrong. (Would delete the question, but can't)
insert into mytable (id1, id2)
select id1, 6 from mytable where id2= 1
(I was struggling with syntax like this:
insert into mytable (id1, id2)
values((SELECT id1 FROM mytable WHERE id2 = 1), 6)
and couldn't figure out how to rearrange it)