sort semicolon separated values per row in a column - oracle11g

I want to sort semicolon separated values per row in a column. Eg.
Input:
abc;pqr;def;mno
xyz;pqr;abc
abc
xyz;jkl
Output:
abc;def;mno;pqr
abc;pqr;xyz
abc
jkl;xyz
Can anyone help?

Perhaps something like this. Breaking it down:
First we need to break up the strings into their component tokens, and then reassemble them, using LISTAGG(), while ordering them alphabetically.
There are many ways to break up a symbol-separated string. Here I demonstrate the use of a hierarchical query. It requires that the input strings be uniquely distinguished from each other. Since the exact same semicolon-separated string may appear more than once, and since there is no info from the OP about any other unique column in the table, I create a unique identifier (using ROW_NUMBER()) in the most deeply nested subquery. Then I run the hierarchical query to break up the inputs and then reassemble them in the outermost SELECT.
with
test_data as (
select 'abc;pqr;def;mno' as str from dual union all
select 'xyz;pqr;abc' from dual union all
select 'abc' from dual union all
select 'xyz;jkl' from dual
)
-- End of test data (not part of the solution!)
-- SQL query begins BELOW THIS LINE.
select str,
listagg(token, ';') within group (order by token) as sorted_str
from (
select rn, str,
regexp_substr(str, '([^;]*)(;|$)', 1, level, null, 1) as token
from (
select str, row_number() over (order by null) as rn
from test_data
)
connect by level <= length(str) - length(replace(str, ';')) + 1
and prior rn = rn
and prior sys_guid() is not null
)
group by rn, str
;
STR SORTED_STR
--------------- ---------------
abc;pqr;def;mno abc;def;mno;pqr
xyz;pqr;abc abc;pqr;xyz
abc abc
xyz;jkl jkl;xyz
4 rows selected.

Related

PLSQL query union sum query

I need to output running balance in the following query, Please help me with the query-code:
select
WEAVING_YARN_TR.TR_ID,
WEAVING_YARN_TR.GP_NO,
WEAVING_YARN_TR.YR_ID,
WEAVING_YARN_GATEPASS.TO_FROM as PARTY,
WEAVING_YARN_GATEPASS.GP_DATE,
WEAVING_YARN_TR.BAGS_IN,
WEAVING_YARN_TR.BAGS_OUT,
SUM(NVL(WEAVING_YARN_TR.BAGS_OUT,0)-NVL(WEAVING_YARN_TR.BAGS_IN,0)) over (order by GP_DATE) as BALANCE
from WEAVING_YARN_TR,WEAVING_YARN_GATEPASS
where WEAVING_YARN_TR.GP_NO=WEAVING_YARN_GATEPASS."GP_NO." and WEAVING_YARN_TR.YR_ID=:P216_YR_ID and WEAVING_YARN_GATEPASS.TO_FROM=:P216_YR_DESC
UNION ALL
select
null as TR_ID,
WEAVING_WARP_SET.SET_ID,
WEAVING_WARP_SET.YARN_ITEM,
WEAVING_WARP_SET.PARTY,
WEAVING_WARP_SET.RECEIVED_DATE,
WEAVING_WARP_SET.TOTAL_BAGS_CONSUMED,
null as BAGS_OUT,
NVL(null,0)-NVL(WEAVING_WARP_SET.TOTAL_BAGS_CONSUMED,0) as BALANCE
from WEAVING_WARP_SET
where WEAVING_WARP_SET.YARN_ITEM=:P216_YR_ID and WEAVING_WARP_SET.PARTY= :P216_YR_DESC
order by GP_DATE
So here is the thing; if you're going to use UNION or UNION ALL, each query block has to be the equal column and type.
For example; We have 2 queries and we want to bind them.
The first query block contains 3 columns and their type NUMBER, VARCHAR, NUMBER (in order). The second block has to be 3 column and types must be NUMBER, VARCHAR, VARCHAR (in order)
So your query does not seem like that. You want an output like the screenshot that you sent. Here is the query that edited:
select
weaving_yarn_gatepass.gp_date
,weaving_yarn_tr.gp_no
,weaving_yarn_gatepass.to_from as party
,weaving_yarn_tr.yr_id
,weaving_yarn_tr.bags_in
,weaving_yarn_tr.bags_out
,sum(nvl(weaving_yarn_tr.bags_out,0) - nvl(weaving_yarn_tr.bags_in,0)) over(order by gp_date) as balance
from weaving_yarn_tr
,weaving_yarn_gatepass
where weaving_yarn_tr.gp_no = WEAVING_YARN_GATEPASS."GP_NO."
and weaving_yarn_tr.yr_id = :p216_yr_id
and weaving_yarn_gatepass.to_from = :p216_yr_desc
union all
select
weaving_warp_set.received_date --I don't know if it means gp_date
,null as gp_no
,weaving_warp_set.party
,null yr_id -- If there is a column named YR_ID in WEAVING_WARP_SET put here
,weaving_warp_set.total_bags_consumed
,null as bags_out
,nvl(null,0) - nvl(weaving_warp_set.total_bags_consumed,0) as balance
from weaving_warp_set
where weaving_warp_set.yarn_item = :p216_yr_id
and weaving_warp_set.party = :p216_yr_desc
order by gp_date;

Remove all the rows except one with the EXCEPT SQLite command

From a dataset character that has a name column, I want to query the two names in with the shortest and longest names, as well as their respective lengths and when there is more than one smallest or largest name, I choose the one that comes first when ordered alphabetically.
With that query, I get all the shortest and longest names (A)
SELECT
name, LENGTH(name) AS LEN
FROM
character
WHERE
length(name) = (SELECT MAX(LENGTH(name)) FROM character)
OR length(name) = (SELECT MIN(LENGTH(name)) FROM character)
With this one, I get all the shortest names except the first alphabetically ordered one (B)
SELECT
name, LENGTH(name) AS LEN
FROM
character
WHERE
length(name) = (SELECT MIN(LENGTH(name)) FROM character)
ORDER BY
name DESC
LIMIT 10 OFFSET 2;
When I try to remove B from A
A EXCEPT B
I would expect to keep the first shortest name but It does not appear.
I would use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY LENGTH(name), name) rn_min,
ROW_NUMBER() OVER (ORDER BY LENGTH(name) DESC, name) rn_max
FROM character
)
SELECT name, LENGTH(name) AS LEN
FROM cte
WHERE 1 IN (rn_min, rn_max)
ORDER BY LENGTH(name);
When you set OFFSET 2 in your B query, you don't get:
all the shortest names except the first 1 alphabetically ordered
Instead you get:
all the shortest names except the first 2 alphabetically ordered,
because this is what OFFSET 2 does: it skips the first 2 rows.
Also another problem with your code is the ORDER BY clause in your B query.
If you have this:
SELECT name,LENGTH(name) AS LEN FROM character
WHERE length(name) = (select max( LENGTH(name)) from character )
or length(name) = (select min( LENGTH(name)) from character)
EXCEPT
SELECT name,LENGTH(name) AS LEN FROM character
WHERE length(name) = (select min( LENGTH(name)) from character)
ORDER BY name desc LIMIT 10 OFFSET 2;
you may think that the ORDER BY clause (and LIMIT and OFFSET) is applied only to your B query, but this is not how it is interpreted.
Actually ORDER BY (and LIMIT and OFFSET) is applied to the whole query after the rows are returned.
To get the results that you want by using code similar to yours you must use a subquery to wrap your B query, like this:
SELECT name,LENGTH(name) AS LEN FROM character
WHERE length(name) = (select max( LENGTH(name)) from character )
or length(name) = (select min( LENGTH(name)) from character)
EXCEPT
SELECT * FROM (
SELECT name,LENGTH(name) AS LEN FROM character
WHERE length(name) = (select min( LENGTH(name)) from character)
ORDER BY name desc LIMIT 10 OFFSET 1
)

order of search for Sqlite's "IN" operator guaranteed?

I'm performing an Sqlite3 query similar to
SELECT * FROM nodes WHERE name IN ('name1', 'name2', 'name3', ...) LIMIT 1
Am I guaranteed that it will search for name1 first, name2 second, etc? Such that by limiting my output to 1 I know that I found the first hit according to my ordering of items in the IN clause?
Update: with some testing it seems to always return the first hit in the index regardless of the IN order. It's using the order of the index on name. Is there some way to enforce the search order?
The order of the returned rows is not guaranteed to match the order of the items inside the parenthesis after IN.
What you can do is use ORDER BY in your statement with the use of the function INSTR():
SELECT * FROM nodes
WHERE name IN ('name1', 'name2', 'name3')
ORDER BY INSTR(',name1,name2,name3,', ',' || name || ',')
LIMIT 1
This code uses the same list from the IN clause as a string, where the items are in the same order, concatenated and separated by commas, assuming that the items do not contain commas.
This way the results are ordered by their position in the list and then LIMIT 1 will return the 1st of them which is closer to the start of the list.
Another way to achieve the same results is by using a CTE which returns the list along with an Id which serves as the desired ordering of the results, which will be joined to the table:
WITH list(id, item) AS (
SELECT 1, 'name1' UNION ALL
SELECT 2, 'name2' UNION ALL
SELECT 3, 'name3'
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
Or:
WITH list(id, item) AS (
SELECT * FROM (VALUES
(1, 'name1'), (2, 'name2'), (3, 'name3')
)
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
This way you don't have to repeat the list twice.

How to get value from oracle stored in || separated?

I am new in oracle and I want to get the value from a column which is stored as "Ashu||123 ||Main Menu|ENG||1|1".
as you can see each value is separated by || symbol.in the above value Ashu is the customer name and 123 is the id, I want both value as customer-name and customer id.
In the query below, I include some test data "on the fly" (not part of the solution; use your actual table name instead of test_data in the main query, and your actual column name instead of str). I included several special cases for testing, to make sure the query works correctly in all cases. I assume the first value (before the first ||) is the customer name and the second the customer id, and the rest of the input string can be ignored. I looked in particular to see that the query handles null values correctly (assuming they may happen in your data).
I left the customer id as a string; if it must be a number, it may be better to wrap it all within to_number().
with
test_data ( str ) as (
select 'Ashu||123||Main Menu|ENG||1|1' from dual union all
select 'Misha||125' from dual union all
select 'Babu||||Main Menu|NZL||?' from dual union all
select 'Rim||' from dual union all
select 'Todd' from dual union all
select '||139||Other Stuff' from dual
)
-- end of test data (only for testing and illustration) - not part of solution
-- SQL query begins BELOW THIS LINE
select str,
regexp_substr(str, '([^|]*)(\|\||$)', 1, 1, null, 1) as cust_name,
regexp_substr(str, '([^|]*)(\|\||$)', 1, 2, null, 1) as cust_id
from test_data
;
STR CUST_NAME CUST_ID
----------------------------- --------- -------
Ashu||123||Main Menu|ENG||1|1 Ashu 123
Misha||125 Misha 125
Babu||||Main Menu|NZL||? Babu
Rim|| Rim
Todd Todd
||139||Other Stuff 139
6 rows selected.

query with max and second factor [duplicate]

I have:
TABLE MESSAGES
message_id | conversation_id | from_user | timestamp | message
I want:
1. SELECT * WHERE from_user <> id
2. GROUP BY conversation_id
3. SELECT in every group row with MAX(timestamp) **(if there are two same timestamps in a group use second factor as highest message_id)** !!!
4. then results SORT BY timestamp
to have result:
2|145|xxx|10000|message
6|1743|yyy|999|message
7|14|bbb|899|message
with eliminated
1|145|xxx|10000|message <- has same timestamp(10000) as message(2) belongs to the same conversation(145) but message id is lowest
5|1743|me|1200|message <- has message_from == me
example group with same timestamp
i want from this group row 3 but i get row 2 from query
SELECT max(message_timestamp), message_id, message_text, message_conversationId
FROM MESSAGES
WHERE message_from <> 'me'
GROUP BY message_conversationId
ORDER by message_Timestamp DESC
what is on my mind to do union from message_id & timestamp and then get max???
Your query is based on non-standard use of GROUP BY (I think SQLite allows that only for compatibility with MySQL) and I'm not at all sure that it will produce determinate results all the time.
Plus it uses MAX() on concatenated columns. Unless you somehow ensure that the two (concatenated) columns have fixed widths, the results will not be accurate for that reason as well.
I would write the query like this:
SELECT
m.message_timestamp,
m.message_id,
m.message_text,
m.message_conversationId
FROM
( SELECT message_conversationId -- for every conversation
FROM messages as m
WHERE message_from <> 'me'
GROUP BY message_conversationId
) AS mc
JOIN
messages AS m -- join to the messages
ON m.message_id =
( SELECT mi.message_id -- and find one message id
FROM messages AS mi
WHERE mi.message_conversationId -- for that conversation
= mc.message_conversationId
AND mi.message_from <> 'me'
ORDER BY mi.message_timestamp DESC, -- according to the
mi.message_id DESC -- specified order
LIMIT 1 -- (this is the one part)
) ;
Try below sql to achieve your purpose by group by twice.
select m.*
from
Messages m
-- 3. and then joining to get wanted output columns
inner join
(
--2. then selecting from this max timestamp - and removing duplicates
select conversation_id, max(timestamp), message_id
from
(
-- 1. first select max message_id in remainings after the removal of duplicates from mix of cv_id & timestamp
select conversation_id, timestamp, max(message_id) message_id
from Messages
where message <> 'me'
group by conversation_id, timestamp
) max_mid
group by conversation_id
) max_mid_ts on max_mid_ts.message_id = m.message_id
order by m.message_id;
http://goo.gl/MyZjyU
ok it was more simple than I thought:
basically to change select from:
max(message_timestamp)
to:
max(message_timestamp || message_id)
or max(message_timestamp + message_id)
so it will search for max on concatenation of timestamp and message_id
ps. after a digging - it's working only if message id is growing with timestamp ( order of insertion is preserved )
edit:
edit2 :
so why it works ?
SELECT max(message_timestamp+message_id), message_timestamp, message_id, message_conversationId, message_from,message_text
FROM MESSAGES
WHERE message_conversationId = 1521521
AND message_from <> 'me'
ORDER by message_Timestamp DESC

Resources