Adding specific number of zeros before a string - oracle

I have written this query
update cmart_m_branch
set code=concat('000',cmart_m_branch.code)
where length(code)=1.
But it is not working..it was updating with all zeros..column code should have only 4 characters. But it is working like this.... just I am changing the query.....
Update cmart_m_branch
set code=concat('00',cmart_m_branch.code)
where length(code)=2.
Why it is not working with the length 1 ..please suggest me..

Sounds like you're after LPAD:
WITH sample_data AS (SELECT '1' col1 FROM dual UNION ALL
SELECT '12' col1 FROM dual UNION ALL
SELECT '123' col1 FROM dual UNION ALL
SELECT '1234' col1 FROM dual)
SELECT col1,
LPAD(col1, 4, '0') new_col1
FROM sample_data;
COL1 NEW_COL1
---- --------
1 0001
12 0012
123 0123
1234 1234
Of course, this is assuming that your cmart_m_branch.code column is of string datatype. If it's of number datatype then there is no use in doing the update - you should instead format the data on-the-fly when you're selecting it (or you could use a virtual column to do the formatting for you). This can be done using to_char():
WITH sample_data AS (SELECT 1 col1 FROM dual UNION ALL
SELECT 12 col1 FROM dual UNION ALL
SELECT 123 col1 FROM dual UNION ALL
SELECT 1234 col1 FROM dual)
SELECT col1,
to_char(col1, '0999') new_col1
FROM sample_data;
COL1 NEW_COL1
---------- --------
1 0001
12 0012
123 0123
1234 1234
If you're absolutely dead-set on using concatenation (why?) then you can do it using a case expression:
WITH sample_data AS (SELECT '1' col1 FROM dual UNION ALL
SELECT '12' col1 FROM dual UNION ALL
SELECT '123' col1 FROM dual UNION ALL
SELECT '1234' col1 FROM dual)
SELECT col1,
CASE WHEN LENGTH(col1) = 1 THEN '000'||col1
WHEN LENGTH(col1) = 2 THEN '00'||col1
WHEN LENGTH(col1) = 3 THEN '0'||col1
ELSE col1
END new_col1
FROM sample_data;
COL1 NEW_COL1
---- --------
1 0001
12 0012
123 0123
1234 1234

Related

SQL | SPLIT COLUMNS INTO ROWS

How can I split the column data into rows with basic SQL.
COL1 COL2
1 A-B
2 C-D
3 AAA-BB
Result
COL1 Col2
1 A
1 B
2 C
2 D
3 AAA
3 BB
From Oracle 12, if it is always two delimited values then you can use:
SELECT t.col1,
l.col2
FROM table_name t
CROSS JOIN LATERAL (
SELECT SUBSTR(col2, 1, INSTR(col2, '-') - 1) AS col2 FROM DUAL
UNION ALL
SELECT SUBSTR(col2, INSTR(col2, '-') + 1) FROM DUAL
) l
Which, for the sample data:
CREATE TABLE table_name (COL1, COL2) AS
SELECT 1, 'A-B' FROM DUAL UNION ALL
SELECT 2, 'C-D' FROM DUAL UNION ALL
SELECT 3, 'AAA-BB' FROM DUAL;
Outputs:
COL1
COL2
1
A
1
B
2
C
2
D
3
AAA
3
BB
db<>fiddle here
Snowflake is tagged, so here's the snowflake way of doing this:
WITH TEST (col1, col2) as
(select 1, 'A-B' from dual union all
select 2, 'C-D' from dual union all
select 3, 'AAA-BB' from dual
)
SELECT test.col1, table1.value
FROM test, LATERAL strtok_split_to_table(test.col2, '-') as table1
ORDER BY test.col1, table1.value;
As of Oracle:
SQL> with test (col1, col2) as
2 (select 1, 'A-B' from dual union all
3 select 2, 'C-D' from dual union all
4 select 3, 'AAA-BB' from dual
5 )
6 select col1,
7 regexp_substr(col2, '[^-]+', 1, column_value) col2
8 from test cross join
9 table(cast(multiset(select level from dual
10 connect by level <= regexp_count(col2, '-') + 1
11 ) as sys.odcinumberlist))
12 order by col1, col2;
COL1 COL2
---------- ------------------------
1 A
1 B
2 C
2 D
3 AAA
3 BB
6 rows selected.
SQL>
For MS-SQL 2016 and higher you can use:
SELECT Col1, x.value
FROM t CROSS APPLY STRING_SPLIT(t.Col2, '-') as x;
BTW: If Col2 contains null, it does not appear in the result.

Select default in case of no value returned

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.
Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>
May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)
You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

row number based on unique column value in oralce?

I do need to implement a oracle sql to have row number defined as below:
row_num, column1, column2, cloumn3
1, ABC, 123, a1
1, ABC, 125, a2
2, ABD, 123, a3
2, ABD, 124, a4
2, ABD, 125, a5
3, ABE, 123, a1
Here I defined row number based on unique value of column1.
Can any one help me to write oracle sql to define row number in this way?
Thanks
Venkat
You need the DENSE_RANK() analytic function:
WITH your_table AS (SELECT 'ABC' col1, 123 col2, 'a1' col3 FROM dual UNION ALL
SELECT 'ABC' col1, 125 col2, 'a2' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 123 col2, 'a3' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 124 col2, 'a4' col3 FROM dual UNION ALL
SELECT 'ABD' col1, 125 col2, 'a5' col3 FROM dual UNION ALL
SELECT 'ABE' col1, 123 col2, 'a1' col3 FROM dual)
-- end of subquery mimicking your_table with data in; See SQL below:
SELECT dense_rank() OVER (ORDER BY col1) row_num,
col1,
col2,
col3
FROM your_table
ORDER BY col1, col2, col3;
ROW_NUM COL1 COL2 COL3
---------- ---- ---------- ----
1 ABC 123 a1
1 ABC 125 a2
2 ABD 123 a3
2 ABD 124 a4
2 ABD 125 a5
3 ABE 123 a1
DENSE_RANK() is similar to RANK() in that it will assign tied rows the same rank (that is, rows that have the same values in the columns being ordered - in your case, col1), but unlike RANK(), DENSE_RANK() won't skip rank numbers.
You can use this as well, but I forgot about dense rank. So Boneist's answer is better.
select rn.row_num , t.* from
(select column1,row_number() over (order by column1) as row_num from
(select distinct column1 column1 from tbl23) )rn
inner join tbl23 t
on rn.column1=t.column1

SQL query to get grouped sums and concatenated lists, preferably without using functions

I have a table like
colA colB colC
A 10 1
A 20 2
A 30 3
B 10 1
B 20 2
I want an output like this
ColA colB colC
A 60 1,2,3
B 30 1,2
Can someone tell me how to do it with and without using functions, and in PL/SQL?
Here is sql you needed.
Note : it will only run on oracle 11g R2 onwards.
with tab as(
select 'A' col1,10 col2,1 col3 from dual union all
select 'A' col1,20 col2,2 col3 from dual union all
select 'A' col1,30 col2,3 col3 from dual union all
select 'B' col1,10 col2,1 col3 from dual union all
select 'B' col1,20 col2,2 col3 from dual )
select col1, sum(col2),listagg(col3,',') WITHIN GROUP (ORDER BY col3) AS col3_list
from tab group by col1
Assume your table name is tab
with tab1(col1, col2) as (
select colA, sum(colB)
from tab group by colA
),
tab2(col1, col2) as (
select colA, LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
)
select a.col1, a.col2, b.col2
from tab1 a, tab2 b
where a.col1 = b.col1
OutPut:
COL1 COL2 COL2
A 60 1,2,3
B 30 1,2
For more information about LISTAGG link
This link explains about multiple with clause
Or simply;
select colA, sum(colB) colB,LISTAGG(TO_CHAR(colC),',')
WITHIN GROUP (ORDER BY colC)
from tab group by colA
Probably this may the easiest solution syntactically :)
SELECT COLA,
SUM(COLB),
WMSYS.WM_CONCAT(COLC)
FROM
(SELECT 'A' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'A' cola,20 AS colb,2 AS colc FROM dual
UNION
SELECT 'A' cola,30 AS colb,3 AS colc FROM dual
UNION
SELECT 'B' cola,10 AS colb,1 AS colc FROM dual
UNION
SELECT 'B' cola,20 AS colb,2 AS colc FROM dual
)
GROUP BY COLA ;
OUTPUT
COLA SUM(COLB) WMSYS.WM_CONCAT(COLC)
A 60 1,2,3
B 30 1,2

SQL filter based on data in multiple rows

I'm finding this a little difficult to describe, but here goes... I have a table with the following:
TITLE_CODE PRODUCT_NUMBER FORMAT_CODE
1234 A1 OC
1234 A2 HB
1234 A3 PB
2345 B1 OC
2345 B2 HB
3456 C1 OC
What I am looking to do is generate a query where I will only pull records that only have an "OC" format code and do not have a "HB" or "PB" format code.
Using Oracle - any help would be greatly appreciated!
SELECT title_code, product_number, format_code
FROM {table}
WHERE format_code = 'OC'
AND title_code NOT IN ( SELECT title_code
FROM {table}
WHERE format_code = 'HB'
OR format_code = 'PB'
);
SELECT TITLE_CODE, PRODUCT_NUMBER FROM {table} WHERE FORMAT_CODE = 'OC';
Something like this?
Here is an alternative, which may or may not be faster for your data than #avk's answer (you'd have to test):
with sample_data (title_code, product_number, format_code) as (select 1234, 'A1', 'OC' from dual union all
select 1234, 'A2', 'HB' from dual union all
select 1234, 'A3', 'PB' from dual union all
select 2345, 'B1', 'OC' from dual union all
select 2345, 'B2', 'HB' from dual union all
select 3456, 'C1', 'OC' from dual union all
select 7890, 'D1', 'OC' from dual union all
select 7890, 'D2', 'TD' from dual)
select title_code,
product_number,
format_code
from (select title_code,
product_number,
format_code,
max(case when format_code = 'OC' then 1 else 0 end) over (partition by title_code) oc,
max(case when format_code = 'PB' then 1 else 0 end) over (partition by title_code) pb,
max(case when format_code = 'HB' then 1 else 0 end) over (partition by title_code) hb
from sample_data)
where oc = 1
and pb = 0
and hb = 0;
TITLE_CODE PRODUCT_NUMBER FORMAT_CODE
---------- -------------- -----------
3456 C1 OC
7890 D1 OC
7890 D2 TD
N.B. I have added in the case where one of the title_codes has a format_code of something other than OC, HB and PB; depending on what you wanted to display, you may want to add in an additional where clause of and format_code = 'OC'. You also haven't said what you'd want displaying if there were two rows with a format_code of OC...

Resources