Creating a query that creates a column from two columns - sql

I'm sorry if the title is not descriptive enough, but I really didn't know how to put it in a sentence. Lets say I have this table:
ID | LOC | NAMEA | NAMEB
------------------------
0 | BL | X | Y
1 | BG | Z | NULL
I want to know if it is possible to write a query that returns this from the table:
ID | LOC | NAME
------------------------
0 | BL | X
0 | BL | Y
1 | BG | Z
I know that if I need to use this, the database is bad and I do not plan on doing this, but I just want to know if it is possible and how.

You can use this-
SELECT ID, LOC, NAMEA AS NAME
FROM your_table
WHERE NAMEA IS NOT NULL
UNION ALL
SELECT ID, LOC, NAMEB AS NAME
FROM your_table
WHERE NAMEB IS NOT NULL

UNION (ALL) helps:
SQL> with test (id, loc, namea, nameb) as
2 (select 0, 'BL', 'X', 'Y' from dual union all
3 select 1, 'BG', 'Z', NULL from dual
4 )
5 select id, loc, namea from test where namea is not null
6 union all
7 select id, loc, nameb from test where nameb is not null
8 order by id;
ID LO N
---------- -- -
0 BL X
0 BL Y
1 BG Z
SQL>

Use union all
select id, loc,nameA from tablename where nameA is not null
union all
select id, loc,nameB from tablename where nameB is not null

You want union all :
select t.*
from (select t.id, t.loc, t.namea as name
from table t
union all
select t.id, t.loc, t.nameb
from table t
) t
where name is not null
order by id;

I don't have a direct query to return the output mentioned above but you can try the following steps. By doing this you will create a new table with the data you require as an output.
step 1:
`create table test2 as (select id,loc,namea from test1);`
step 2:create table test3 as (select id,loc,nameb from test1);
step 3:insert into test2(id,loc,namea) select * from test3 where nameb='y';
step 4: select * from test2
One more thing namea,nameb should have same data size.

Related

Sequence generation for new ID

I have a requirement where there are 2 tables A and B. There is a column named ID which is a primary key for A and foreign key for table B where the tables have one to many relationship between them. In table A for ID column we have entries 1,2,3 and for them we have corresponding multiple entries for one ID and another column in table B named SEQ where a sequence has been created for entries in it. Now for entries for ID 1, I have 3 entries in table B with SEQ 1,2,3 but when new ID entry will be there then i need the sequence again to start from 1 for that ID.**Can you please help me to do that.
TABLE A
I'd suggest you not to store the SEQ value. Why would you? It is easy to calculate it whenever needed. How? Like this, using row_number analytic function:
SQL> with b (id, name) as
2 (select 1, 'TRI' from dual union all
3 select 1, 'TRI' from dual union all
4 select 1, 'TRI' from dual union all
5 select 2, 'ROHIT' from dual union all
6 select 2, 'ROHIT' from dual union all
7 select 3, 'RAVI' from dual
8 )
9 select id,
10 name,
11 row_number() over (partition by id order by null) seq
12 from b;
ID NAME SEQ
---------- ----- ----------
1 TRI 1
1 TRI 2
1 TRI 3
2 ROHIT 1
2 ROHIT 2
3 RAVI 1
6 rows selected.
SQL>
If you still want to store it, now you know how.
Don't have multiple sequences. Just use a single sequence in the B table and accept that there will be gaps for each ID and then if you need sequential values you can calculate them as needed using the ROW_NUMBER() analytic function and, if you need to, put it in a view. Also, don't duplicate the name from table A in table B; keep your data normalised.
CREATE TABLE A (
id NUMBER(8,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT A__id__pk PRIMARY KEY,
name VARCHAR2(20)
);
CREATE TABLE B (
id NUMBER(8,0)
CONSTRAINT B__id__nn NOT NULL
CONSTRAINT B__id__fk REFERENCES A(id),
seq NUMBER(8,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT B__seq__pk PRIMARY KEY
);
Then you can create your sample data:
INSERT INTO A ( name )
SELECT 'TRI' FROM DUAL UNION ALL
SELECT 'ROHIT' FROM DUAL UNION ALL
SELECT 'RAVI' FROM DUAL;
INSERT INTO B ( id )
SELECT 1 FROM DUAL UNION ALL
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 1 FROM DUAL;
And:
SELECT *
FROM B
Outputs:
ID | SEQ
-: | --:
1 | 1
1 | 2
2 | 3
3 | 4
2 | 5
1 | 6
If you want your output then create a view:
CREATE VIEW B_view ( id, name, seq ) AS
SELECT b.id,
a.name,
ROW_NUMBER() OVER ( PARTITION BY b.id ORDER BY seq )
FROM B
INNER JOIN A
ON ( B.id = A.id )
Then:
SELECT *
FROM b_view
Outputs:
ID | NAME | SEQ
-: | :---- | --:
1 | TRI | 1
1 | TRI | 2
1 | TRI | 3
2 | ROHIT | 1
2 | ROHIT | 2
3 | RAVI | 1
db<>fiddle here

Select ALL records with fieldvalue1 if any records (with fieldvalue1) have fieldvalue3 have a specific value

I need to show all records for a specific value if ANY one of those records have another specific value. Essentially, if field3 = 'b', what is field1? Show all records with value of field1 regardless of their field3 value.
Record Number External Id Letter
1 123456 a
2 123456 b
3 123456 c
4 456852 t
5 456852 b
record 2 has a letter value of 'b' - so I want to look at externalid, which is 123456, now I want to pull all records for external id regardless if the other records have a letter value of 'b'
Use EXISTS and a correlated subquery:
SELECT *
FROM mytable t
WHERE
t.letter = 'b'
OR EXISTS (
SELECT 1
FROM mytable t1
WHERE
t1.record_number != t.record_number
AND t1.external_id = t.external_id
AND t1.letter = 'b'
)
Another option is to use a window function:
SELECT record_number, external_id, letter
FROM (
SELECT
t.*,
MAX(CASE WHEN letter = 'b' THEN 1 END) OVER(PARTITION BY external_id) mx
FROM mytable t
) x WHERE mx = 1
Demo on DB Fiddle:
record_number | external_id | letter
------------: | ----------: | :-----
1 | 123456 | a
2 | 123456 | b
3 | 123456 | c
4 | 456852 | t
5 | 456852 | b
Use exists, but don't worry about filtering in the outer query:
select t.*
from t
where exists (select 1
from t t2
where t2.external_id = t.external_id and t2.letter = 'b'
);
With an index on (external_id, letter), I would expect this to have very good performance.

joining table with multiple rows and same column name

table 1
id | name | gender
1 | ABC | M
2 | CDE | M
3 | FGH | M
table 2
id | name | gender
4 | BAC | F
5 | DCE | F
6 | GFH | F
how to make output in oracle database like this :
id | name | gender
1 | ABC | M
2 | CDE | M
3 | FGH | M
4 | BAC | F
5 | DCE | F
6 | GFH | F
Use UNION [ALL]:
select * from table1
union all
select * from table2;
P.S. If there exists any duplicated row for individual SELECT statements, UNION would remove duplicates, but UNION ALL concatenates rows even they are duplicates.
If you really need to "join" 2 tables:
with a as (
select 1 id, 'ABC' name, 'M' gender from dual union all
select 2 id, 'CDE' name, 'M' gender from dual union all
select 3 id, 'FGH' name, 'M' gender from dual ),
b as (
select 4 id, 'BAC' name, 'F' gender from dual union all
select 5 id, 'DCE' name, 'F' gender from dual union all
select 6 id, 'GFH' name, 'F' gender from dual )
select coalesce(a.id, b.id) id,
coalesce(a.name, b.name) name,
coalesce(a.gender, b.gender) gender
from a
full join b
on a.id = b.id
/* if name, gender not in pk */
-- and a.name = b.name
-- and a.gender = b.gender
;
In this case all duplicated "ID"s will be removed. And first not null value of "name", "gender" columns will be returned becouse of coalesce function.
You can even use greatest, least and ets, instead of coalesce..
p.s. Be careful if you don't have PK on table!

SQL statement to conditionally selecting records based on the previous record

I have 2 tables as below
Table 1 : Animal (ID is a primary key)
ID |Animal
----------
1 |Dog
2 |Cat
3 |Fish
4 |Bird
5 |Elephant
Table 2: Pet (ID here is foreign keys to the Animal table)
ID | Animal | Name
----------
1 | Dog | Annie
1 | Dog | Buckie
2 | Cat | Conner
2 | Cat | Kitten
3 | Fish| Lala
I want to write a SQL statement to append a row with "Fish" right after wherever a specific pet "Dog" appears without breaking the order.
Expected result should be:
ID | Animal | Name
----------
1 | Dog | Annie
3 | Fish| NULL
1 | Dog | Buckie
3 | Fish| NULL
2 | Cat | Conner
2 | Cat | Kitten
3 | Fish| Lala
I'm not too sure about Oracle11g but I think it has ROW_NUMBER.
You could add a row number to the original table,
and then union a fish table with corresponding row numbers.
For example
WITH Tablex AS (
SELECT ROW_NUMBER() OVER(ORDER BY ID, Name) AS ref_id, *
FROM your_table
)
SELECT ID, Animal, Name
FROM (SELECT *
FROM Tablex
UNION ALL
SELECT *
FROM
(SELECT ref_id, 3 AS ID, 'Fish' AS Animal, NULL AS Name
FROM TableX
WHERE Animal = 'Dog'
) x
) X
ORDER BY ref_id, id
As commented above, the order of rows depends only on the ORDER BY clause and the order may not be actually incorporated in the table (if you put it INTO something).
Try this
select tn.Id
, case when tt.rn = 0 then tn.Animal else 'Fish' end Animal
, case when tt.rn = 0 then tn.Name else NULL end Name
, tn.rn+tt.rn rn
from (
select ID, Animal, Name, 2 * row_number() over (order by id, name) as rn
from pet
) tn
join (
select 0 rn from dual union
select 1 from dual
) tt on tt.rn <= case Animal when 'Dog' then 1 else 0 end
order by tn.rn+tt.rn;
with Q as (
select ID, Animal, Name,
row_number() over (order by id, name) rnum
from Pet
)
select ID, Animal, Name, rnum
from Q
union all
select 3, 'Fish', NULL, rnum+0.5
from Q
where ID=1 and name in('Annie','Buckie')
order by rnum

How do I print out 'NULL' or '0' values for column values when an element isn't found?

I need to loop through a set of values (less than 10) and see if they are in a table. If so, I need to print out all of the record values, but if the item doesn't exist, I still want it to be included in the printed result, although with NULL or 0 values. So, for example, the following query returns:
select *
from ACTOR
where ID in (4, 5, 15);
+----+-----------------------------+-------------+----------+------+
| ID | NAME | DESCRIPTION | ORDER_ID | TYPE |
+----+-----------------------------+-------------+----------+------+
| 4 | [TEST-1] | | 3 | NULL |
| 5 | [TEST-2] | | 4 | NULL |
+----+-----------------------------+-------------+----------+------+
But I want it to return
+----+-----------------------------+-------------+----------+------+
| ID | NAME | DESCRIPTION | ORDER_ID | TYPE |
+----+-----------------------------+-------------+----------+------+
| 4 | [TEST-1] | | 3 | NULL |
| 5 | [TEST-2] | | 4 | NULL |
| 15| NULL | | 0 | NULL |
+----+-----------------------------+-------------+----------+------+
Is this possible?
To get the output you want, you first have to construct a derived table containing the ACTOR.id values you desire. UNION ALL works for small data sets:
SELECT *
FROM (SELECT 4 AS actor_id
FROM DUAL
UNION ALL
SELECT 5
FROM DUAL
UNION ALL
SELECT 15
FROM DUAL) x
With that, you can OUTER JOIN to the actual table to get the results you want:
SELECT x.actor_id,
a.name,
a.description,
a.orderid,
a.type
FROM (SELECT 4 AS actor_id
FROM DUAL
UNION ALL
SELECT 5
FROM DUAL
UNION ALL
SELECT 15
FROM DUAL) x
LEFT JOIN ACTOR a ON a.id = x.actor_id
If there's no match between x and a, the a columns will be null. So if you want orderid to be zero when there's no match for id 15:
SELECT x.actor_id,
a.name,
a.description,
COALESCE(a.orderid, 0) AS orderid,
a.type
FROM (SELECT 4 AS actor_id
FROM DUAL
UNION ALL
SELECT 5
FROM DUAL
UNION ALL
SELECT 15
FROM DUAL) x
LEFT JOIN ACTOR a ON a.id = x.actor_id
Well, for that few values, you could do something ugly like this, I suppose:
SELECT
*
FROM
(
SELECT 4 AS id UNION
SELECT 5 UNION
SELECT 15
) ids
LEFT JOIN ACTOR ON ids.id = ACTOR.ID
(That should work in MySQL, I think; for Oracle you'd need to use DUAL, e.g. SELECT 4 as id FROM DUAL...)
That is only possible using a temporary table.
CREATE TABLE actor_temp (id INTEGER);
INSERT INTO actor_temp VALUES(4);
INSERT INTO actor_temp VALUES(5);
INSERT INTO actor_temp VALUES(15);
select actor_temp.id, ACTOR.* from ACTOR RIGHT JOIN actor_temp on ACTOR.id = actor_temp.id;
DROP TABLE actor_temp;
If you know the upper and lower limits on the ID, it's not too bad. Set up a view with all possible ids - the connect by trick is the simplest way - and do an outer join with your real table. Here, I've limited it to values from 1-1000.
select * from (
select ids.id, a.name, a.description, nvl(a.order_id,0), a.type
from Actor a,
(SELECT level as id from dual CONNECT BY LEVEL <= 1000) ids
where ids.id = a.id (+)
)
where id in (4,5,15);
Can you make a table that contains expected actor ids?
If so you can left join from it.
SELECT * FROM expected_actors LEFT JOIN actors USING (ID)