Here is my example,
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select * from TABLE1;
From above table1 I want to create a view as follows,
ID | COLTYPE | col | SUBCOLTYPE | subcol
-------------------------------------------------------------
1 COL1 m1 SUBCOL1 s1
2 COL2 m2 SUBCOL2 s2
what I did was I merged COL1 , COL2 in to COL and SUBCOL1, SUBCOL2 in to SUBCOL. Can I achieve this by using UNPIVOT() function.
my imaginary query is as follows,
select * from table1
unpivot(COL for COLTYPE in (COL1,COL2)) --- FIRST MERGE
unpivot(SUBCOL FOR SUBCOLTYPE IN (SUBCOL1,SUBCOL2)) ---SECOND MERGE
;
Each first and second merge lines are working individually when comment other one. But They are not working same time. How to add several unpivot() function in the same select statement. Is it possible to do ?
Answer to the original question
Why do you want to use unpivot? I ask because you are not transposing columns into rows. Is it guaranteed that all the columns you want to merge are NULL except one of them? If yes you could use the coalesce function.
Source data:
ID COL1 COL2 SUBCOL1 SUBCOL2
---------- -------- -------- -------- --------
1 m1 s1
2 m2 s2
Example:
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select id,
coalesce(col1,col2) as col,
coalesce(subcol1, subcol2) as subcol
from TABLE1;
Result:
ID COL SUBCOL
---------- -------- --------
1 m1 s1
2 m2 s2
Answer to the updated question
After you edited your question, this syntax is probably what you are looking for:
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select id, coltype, col, subcoltype, subcol from TABLE1
UNPIVOT ((col, subcol) FOR (coltype, subcoltype) IN ((col1, subcol1) AS ('col1', 'subcol1'), (col2, subcol2) AS ('col2', 'subcol2')));
Result:
ID COLTYPE COL SUBCOLTYPE SUBCOL
---------- ---------------- -------- ---------------------------- --------
1 col1 m1 subcol1 s1
2 col2 m2 subcol2 s2
Related
table TEST
id
Name
1
abc
2
xyz
In general i used to get records from below query
Select id,name from TEST.
id
Name
1
abc
2
xyz
but now i want to create a duplicate for each row on top my select query
expected output: please suggest how can i achieve result like below
id
Name
1
abc
1
abc
2
xyz
2
xyz
You may cross join your table with a sequence table containing how ever many copies you want. Here is an example using an inline sequence table:
SELECT t1.id, t1.Name
FROM yourTable t1
CROSS JOIN (
SELECT 1 AS seq FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT 3 FROM dual
) t2
WHERE t2.seq <= 2
ORDER BY t1.id;
To me, UNION (ALL) set operator seems to be quite simple.
Sample data:
SQL> select * from test;
ID NAME
---------- ----
1 abc
2 xyz
UNION ALL:
SQL> select * from test
2 union all
3 select * from test;
ID NAME
---------- ----
1 abc
2 xyz
1 abc
2 xyz
SQL>
CREATE table test(
id integer,
name VARCHAR2(4)
);
INSERT into test (id, name) VALUES (1,'ABC');
INSERT into test (id, name) VALUES (2,'XYZ');
with data as (select level l from dual connect by level <= 2)
select *
from test, data
order by id, l
/
One more option is LATERAL
SELECT t.*
FROM test
, LATERAL (
SELECT id, name FROM DUAL
union all
SELECT id, name FROM DUAL
) t
One option is using a self-join along with ROW_NUMBER analytic function such as
WITH t AS
(
SELECT t1.id, t1.name, ROW_NUMBER() OVER (PARTITION BY t1.id ORDER BY 0) AS rn
FROM test t1,
test t2
)
SELECT id, name
FROM t
WHERE rn <= 2
Demo
I have a unique list of strings (the original idea was the column names in a table).
The task is to perform a maximal possible abbreviation of the list, so the list remains distinct.
For example AAA, AB can be abbreviated to AA, AB. (But not to A, AB – as A could be prefix of both AAA and AB).
AAAA, BAAAA can be shorten to A, B.
But A1, A2 can’t be abbreviated at all.
Here are the sample data
create table tab as
select 'AAA' col from dual union all
select 'AABA' col from dual union all
select 'COL1' col from dual union all
select 'COL21' col from dual union all
select 'AAAAAA' col from dual union all
select 'BBAA' col from dual union all
select 'BAAAA' col from dual union all
select 'AB' col from dual;
The expected result is
COL ABR_COL
------ ------------------------
AAA AAA
AAAAAA AAAA
AABA AAB
AB AB
BAAAA BA
BBAA BB
COL1 COL1
COL21 COL2
I managed a brute force solution consisting of four subqueries, which I do not post on purpose, because I hope there exists a more simple solution from which I do not want to distract.
Btw there is a similar function in r called abbreviate, but I’m looking for SQL solution. Prefered Oracle solutions for other RDBMS are welcommed.
I would do the filtering in the recursive CTE:
with potential_abbreviations(col, abbr, lev) as (
select col, col as abbr, 1 as lev
from tab
union all
select pa.col, substr(pa.abbr, 1, length(pa.abbr) - 1) as abbr, lev + 1
from potential_abbreviations pa
where length(abbr) > 1 and
not exists (select 1
from tab
where tab.col like substr(pa.abbr, 1, length(pa.abbr) - 1) || '%' and
tab.col <> pa.col
)
)
select pa.col, pa.abbr
from (select pa.*, row_number() over (partition by pa.col order by pa.lev desc) as seqnum
from potential_abbreviations pa
) pa
where seqnum = 1
Here is a db<>fiddle.
The lev is strictly not necessary. You can use length(abbr) desc in the order by. But, I usually include a recursion counter when I use recursive CTEs, so this is habit.
Doing the extra comparison in the CTE may look more complicated, but it simplifies the execution -- the recursion stops at the correct value.
This is also tested on unique single letter col values.
This is actually possible using a recursive CTE. I don't really get it shorter than three subqueries (plus one query), but at least it is not constrained by string length. The steps are roughly as follows:
Calculate all potential abbreviations with a recursive CTE. This selects all column
names themselves and then the column names shortened by one letter, recursively:
Table:
col abbr
--- -------
AAA AAA
AAA AA
AAA A
...
For each abbreviation, count how often it occurs
Table
ABBR CONFLICT
---- --------
AA 3
AAA 2
AABA 1
...
Select the abbreviations that are the unique shortest ones, and also
the abbreviations that are just the column name itself, and rank these by length of the abbreviation. In the example, you see that AAA conflicts with some other abbreviation but still must be chosen as it is equal to its unshortened name.
Table
COL ABBR CONFLICT POS
-------------------------------
AAA AAA 2 1
AAAAAA AAAA 1 1
AAAAAA AAAAA 1 2
AAAAAA AAAAAA 1 3
AABA AAB 1 1
...
Choose the first ranked abbreviation (or column name itself) for each column.
Table
COL ABBR POS
-------------------
AAA AAA 1
AAAAAA AAAA 1
AABA AAB 1
...
Complete SQL
This results in the following SQL, with the above steps as CTEs:
with potential_abbreviations(col,abbr) as (
select
col
, col as abbr
from tab
union all
select
col
, substr(abbr, 1, length(abbr)-1 ) as abbr
from potential_abbreviations
where length(abbr) > 1
)
, abbreviation_counts as (
select abbr
, count(*) as conflict
from potential_abbreviations
group by abbr
)
, all_unique_abbreviations(col,abbr,conflict,pos) as (
select
p.col
, p.abbr
, conflict
, rank() over (partition by col order by p.abbr) as pos
from potential_abbreviations p
join abbreviation_counts c on p.abbr = c.abbr
where conflict = 1 or p.col = p.abbr
)
select col, abbr, pos
from all_unique_abbreviations
where pos = 1
order by col, abbr
Result
COL ABBR
------- ----
AAA AAA
AAAAAA AAAA
AABA AAB
AB AB
AC1 AC
AD AD
BAAAA BA
BBAA BB
COL1 COL1
COL21 COL2
SQL Fiddle
I found a second approach, not added to the first answer as it is shorter and different. The steps are as follows:
Calculate all potential abbreviations for each name, recursively
SQL
select
col
, col as abbr
from tab
union all
select
col
, substr(abbr, 1, length(abbr)-1 ) as abbr
from potential_abbreviations a
where length(abbr) > 1
Results
col abbr
--- -------
AAA AAA
AAA AA
AAA A
...
Then calculate the conflicts between abbreviations. Also keep track of the column name that led to this abbreviation. We only want to keep abbreviations that cause no conflict, so the min() aggregate is of no concern.
SQL
select
abbr
, count(*) as conflicts
, min(col) as best_candidate
from potential_abbreviations
group by abbr
having count(*) = 1
Result
ABBR CONFLICTS BEST_CANDIDATE
------- --------- ---------------
AAAA 1 AAAAAA
AAAAA 1 AAAAAA
AAAAAA 1 AAAAAA
AAB 1 AABA
AABA 1 AABA
...
Finally, do a left join of the potential abbreviations with the best conflict-free candidates, and just use the column name if there was no conflict free resolution:
SQL
select
p.col as col
, nvl(min(c.abbr), p.col) as abbr
from potential_abbreviations p
left join conflict_free c on p.col = c.best_candidate
where c.conflicts = 1 or p.abbr = p.col
group by p.col
order by col, abbr
Complete SQL
with potential_abbreviations(col,abbr) as (
select
col
, col as abbr
from tab
union all
select
col
, substr(abbr, 1, length(abbr)-1 ) as abbr
from potential_abbreviations a
where length(abbr) > 1
)
, conflict_free as (
select
abbr
, count(*) as conflicts
, min(col) as best_candidate
from potential_abbreviations
group by abbr
having count(*) = 1
)
select
p.col as col
-- , c.best_candidate
, nvl(min(c.abbr), p.col) as abbr
-- , min(c.abbr) over (partition by c.best_candidate) shortest
from potential_abbreviations p
left join conflict_free c on p.col = c.best_candidate
where c.conflicts = 1 or p.abbr = p.col
group by p.col, c.best_candidate
order by col, abbr
Result
COL ABBR
------- ----
AAA AAA
AAAAAA AAAA
AABA AAB
AB AB
AC1 AC
AD AD
BAAAA BA
BBAA BB
COL1 COL1
COL21 COL2
SQL Fiddle
Note: For Postgresql, the recursive CTE must be with recursive while Oracle does not like the word recursive at all there.
I'm at my first question on stackexchange, as i have a few days since I struggle on this matter:
I want to make a complex query(PLSQL) on a table that has col1,col2,col3,col4,col5 having values like (names: which are split- one part per column)
+------+--------+--------+--------+------+
| ID | Col1 | Col2 | Col3 | Col4 |
+------+--------+--------+--------+------+
| (#1) | Andrew | Joan | Bach | Mike |
| (#2) | Mark | Andrew | Livy | |
| (#3) | Joan | Arch | Donnie | |
| (#4) | Joan | Andrew | Lyx | |
+------+--------+--------+--------+------+
Number of name parts differ from 1 to 5.
I want to search in different combinations like this:
search for Bach Joan Mike - get #1, #3,#4 - in this order
search for Andrew Bach - get the following order #1,#2,#4 - in this order
I don't like the idea of using a looooong query in which I will write all possible permutations in order to mach each part of my search string
What I'd like to achieve is this:
first set: match all those n parts (both Bach and Joan and Bach match, in any order)
second set: match n-1 parts (at least N-1 of my search parts match the row, in any order)
third set: match n-2 parts
I use a ORACLE database and I was thinking on making it in a stored procedure: match_my_set(query_str,col1,col2,col3,col4,col5). I would write at least 5 loops (loop into loop) in order to achieve this, but I doubt it is a professional idea . .
Any help is appreciated. Thank you
If you're on 11g or higher, you can unpivot your columns into rows; this is using a CTE to provide your sample data:
with t (id, col1, col2, col3, col4, col5) as (
select 1, 'Andrew', 'Joan', 'Bach', 'Mike', null from dual
union all select 2, 'Mark', 'Andrew', 'Livy', null, null from dual
union all select 3, 'Joan', 'Arch', 'Donnie', null, null from dual
union all select 4, 'Joan', 'Andrew', 'Lyx' , null, null from dual
)
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5));
ID COL_NO NAME
---------- ---------- ------
1 1 Andrew
1 2 Joan
1 3 Bach
1 4 Mike
2 1 Mark
2 2 Andrew
2 3 Livy
...
Then you can look for matches against the single name column:
select distinct id
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
order by id;
ID
----------
1
3
4
I think you want to make the ordering more complicated though, by counting how many of the terms match in each row. If so you can do:
select id, count(*) as cnt
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
group by id;
ID CNT
---------- ----------
1 3
4 1
3 1
and then have another level of inline view to order by the count, with some way to break ties:
select id
from (
select id, count(*) as cnt
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
group by id
)
order by cnt desc, id;
Which gets the same result with your sample data. Changing the IN condition to user ('Andrew', 'Bach') also gets 1,2,4 with both versions.
Depending on how you're getting the values you're searching for, you might want to use an array instead (via a table collection expression and a join), or tokenise a string containing all the search words, or some other variation.
You can do it using Oracle's collections (which should work in 10g or later)
Oracle Setup:
CREATE TABLE TABLE_NAME( ID, Col1, Col2, Col3, Col4 ) AS
SELECT 1, 'Andrew', 'Joan', 'Bach', 'Mike' FROM DUAL UNION ALL
SELECT 2, 'Mark', 'Andrew', 'Livy', NULL FROM DUAL UNION ALL
SELECT 3, 'Joan', 'Arch', 'Donnie', NULL FROM DUAL UNION ALL
SELECT 4, 'Joan', 'Andrew', 'Lyx', NULL FROM DUAL;
CREATE TYPE stringlist AS TABLE OF VARCHAR2(100);
/
Query:
SELECT id,
col1,
col2,
col3,
col4
FROM (
SELECT t.*,
stringlist( col1, col2, col3, col4 )
MULTISET INTERSECT
stringlist( 'Bach', 'Joan', 'Mike' ) -- Search terms
AS names
FROM TABLE_NAME t
)
WHERE names IS NOT EMPTY
ORDER BY CARDINALITY( names ) DESC, ID;
Output:
ID COL1 COL2 COL3 COL4
---------- ------ ------ ------ ----
1 Andrew Joan Bach Mike
3 Joan Arch Donnie
4 Joan Andrew Lyx
This is untested, but I think it'll work. First, you need a function that will split a search string by spaces into a table:
CREATE function [dbo].[SplitSpace] (#StringList varchar(4000))
RETURNS #Result Table(Value varchar(50))
AS
BEGIN
DECLARE #x XML
SELECT #X = CAST('<A>' + REPLACE(#StringList, ' ', '</A><A>') + '</A>' AS XML)
INSERT INTO #Result
SELECT t.value('.', 'varchar(50)') as inVal
FROM #X.nodes('/A') AS x(t)
RETURN
END
This function will come in handy elsewhere, too, and you can easily modify it to split by commas or any other value, if needed.
Next, you need to create a query that returns the desired results (when you're done testing, you can convert this to a stored procedure and accept the search string as a parameter):
DECLARE #SearchString varchar(255) = 'Bach Joan Mike'
DECLARE #SearchTable TABLE(Value varchar(50))
INSERT INTO #SearchTable
SELECT DISTINCT Value
FROM SplitSpace(#SearchString)
SELECT DISTINCT Col1, Col2, Col3, Col4
FROM MyTable M
JOIN #SearchTable S
ON S.Value = Col1
OR S.Value = Col2
OR S.Value = Col3
OR S.Value = Col4
CROSS APPLY
(
SELECT COUNT(*) AS [Number of Hits]
FROM #SearchTable
WHERE Value = M.Col1
OR Value = M.Col2
OR Value = M.Col3
OR Value = M.Col4
) t
ORDER BY t.[Number of Hits] DESC
Basically, you're saying "give me all the records where one or more of the "name" columns exists in the search string.
Then, you're saying, with the Cross Apply, "for each row, tell me how many hits I have". Then, all you have to do is order by the number of hits and you're all set.
Note: it's possible that someone could enter "Andrew Andrew Bach" as a search string. If they did so, you would get 2 hits for each column that has Andrew in it and only one for each one that had Bach. That is why you select distinct value from the function-returned table; it eliminates those duplicates. Also, if you have multiple matches, you would get that record returned twice, since it's an inner join, so you select distinct col1, col2, col3, col4 from those results, to eliminate those duplicates, as well.
Please let me know if you have any questions.
First of all appologies if the question is below standard(basic for most of all).
I have a column whose value gets incremented automatically based on other column.E.g.:if column name has value "abc","abc" ,"xyz","xyz",xyz","hij" the value in auto-incremented column must be "1","2","1","2","3","1".
The problem occurs while deleating or updating record.
What if someone delete ["xyz"] value of having value "2"?
How to handle such situation ?
As one of the options(simple and straightforward one) you can create a view and generate that "auto-incremented column" on the fly - every time you query the view.
Here is an example:
-- source table, which does not contain that auto incremented
-- column you are interested in
create table t1(id, col1) as (
select 1, 'abc' from dual union all
select 2, 'abc' from dual union all
select 3, 'xyz' from dual union all
select 4, 'xyz' from dual union all
select 5, 'xyz' from dual union all
select 6, 'hij' from dual
);
-- and here is the view
create or replace view t1_v as
select id
, col1
, row_number() over(partition by col1
order by id) as auto_inc
from t1;
select *
from t1_v;
ID COL1 AUTO_INC
---------- ---- ----------
1 abc 1
2 abc 2
6 hij 1
3 xyz 1
4 xyz 2
5 xyz 3
Updating the value:
-- Update can be issued against base table or
-- a view, if it's a key-preserved one
update t1
set col1 = 'acb'
where id = 1;
select *
from t1_v;
ID COL1 AUTO_INC
---------- ---- ----------
2 abc 1
1 acb 1
6 hij 1
3 xyz 1
4 xyz 2
5 xyz 3
Deleting a row:
-- You can delete from the base table or
-- a view, if it's a key-preserved one
delete from t1
where id = 4;
select *
from t1_v;
ID COL1 AUTO_INC
---------- ---- ----------
2 abc 1
1 acb 1
6 hij 1
3 xyz 1
5 xyz 2
How to select newest record from two tables using SQL?
"select * from Table1,Table2 WHERE Date=(SELECT MAX(Date) FROM Table1,Table2)"
----------- -------------
| table1 | | table2 |
----------- -------------
----------- -------------
| title | | title |
----------- -------------
| text | | text |
----------- -------------
| date | | date |
----------- -------------
This will do it:
SELECT TOP 1 * FROM
(
SELECT * FROM Table_1
UNION ALL
SELECT * FROM Table_2
)
AS ALL_RECORDS
ORDER BY Date DESC
Try something like:
with tmp(title, text, date) as
(
select title, text, date from table1
union
select title, text, date from table2
)
select top 1 * from tmp
order by date desc
This should solve your problem.
SELECT * FTOM Table1, Tble2... creates a cross join (cartesian product of two sets of records) so ther will be multiple records with the same date. You have to specify more criteria to get only one record, and probably use some join.
If you want to choose one record from two tables, where for example Table1 has the newer record than Table2, I think it will be good idea to use union, e.g.
SELECT col1, col2, col3, col4, ..., coln max(Date) FROM (
SELECT * FROM Table1 UNION
SELECT * FROM Table2
) GROUP BY col1, col2, col3, col4, ..., coln
ORDER BY Date;