Cursors from multiple tables plsql - loops

need some help with the following task.
I have 3 tables: active_t, company_t, element_t
active_t has 2 columns: instancename and active. It basically contains a list of schemas and active will either be 1(meaning active) or 0 (not active).
I need to query this table to fetch list of active schemas. Then based on each active schema I need to find list of active company codes (from column comp_code) from company_t. Then for each active company I need to insert data from the table element_t into a 4th table (target_t);
Point to note here is that the tables company_t and element_t exist in all schemas.
I tried to first create a cursor c1 like : select instancename from active_t where active=1;
but now not sure how to use this cursor for the next steps.
Help will be greatly appreciated.
One thing that I missed to add is that we have created a separate schema "TESTUSER" which has the table active_t. While the tables company_t and element_t are existing in each country specific schema. This TESTUSER has been given select grant on company_t and element_t.
The table active_t looks like this:
INSTANCENAME ACTIVE
IN 1
SI 0
company_t looks like this (for active country IN)
CODE DELDATE
1111 null
2222 null
3333 null
4444 null
The code will first have to check this table and go for the schema IN( since active=1). For this schema the code will have to then get a list of active companies from the table company_t from that country schema (IN in this case).
Company_t and element_t does not have a column which identifies the schema.
Thanks.

If we take as sample data the folowing CTEs:
WITH
active_t AS
(
Select 'A' "INSTANCE_NAME", 0 "IS_ACTIVE" From Dual Union All
Select 'B' "INSTANCE_NAME", 1 "IS_ACTIVE" From Dual Union All
Select 'C' "INSTANCE_NAME", 1 "IS_ACTIVE" From Dual Union All
Select 'D' "INSTANCE_NAME", 0 "IS_ACTIVE" From Dual Union All
Select 'E' "INSTANCE_NAME", 1 "IS_ACTIVE" From Dual
),
company_t AS
(
Select 'A' "INSTANCE_NAME", 101 "COMPANY_CODE" From Dual Union All
Select 'A' "INSTANCE_NAME", 102 "COMPANY_CODE" From Dual Union All
Select 'B' "INSTANCE_NAME", 201 "COMPANY_CODE" From Dual Union All
Select 'B' "INSTANCE_NAME", 202 "COMPANY_CODE" From Dual Union All
Select 'B' "INSTANCE_NAME", 203 "COMPANY_CODE" From Dual Union All
Select 'C' "INSTANCE_NAME", 301 "COMPANY_CODE" From Dual Union All
Select 'D' "INSTANCE_NAME", 401 "COMPANY_CODE" From Dual Union All
Select 'D' "INSTANCE_NAME", 402 "COMPANY_CODE" From Dual Union All
Select 'E' "INSTANCE_NAME", 501 "COMPANY_CODE" From Dual
),
element_t AS
(
Select 101 "COMPANY_CODE", 'EL_11_A' "ELEMENT_ID" From Dual Union All
Select 101 "COMPANY_CODE", 'EL_12_A' "ELEMENT_ID" From Dual Union All
Select 101 "COMPANY_CODE", 'EL_13_A' "ELEMENT_ID" From Dual Union All
Select 202 "COMPANY_CODE", 'EL_21_B' "ELEMENT_ID" From Dual Union All
Select 203 "COMPANY_CODE", 'EL_22_B' "ELEMENT_ID" From Dual Union All
Select 301 "COMPANY_CODE", 'EL_31_C' "ELEMENT_ID" From Dual Union All
Select 401 "COMPANY_CODE", 'EL_41_D' "ELEMENT_ID" From Dual Union All
Select 401 "COMPANY_CODE", 'EL_42_D' "ELEMENT_ID" From Dual Union All
Select 401 "COMPANY_CODE", 'EL_44_D' "ELEMENT_ID" From Dual Union All
Select 401 "COMPANY_CODE", 'EL_45_D' "ELEMENT_ID" From Dual Union All
Select 401 "COMPANY_CODE", 'EL_46_D' "ELEMENT_ID" From Dual Union All
Select 501 "COMPANY_CODE", 'EL_51_E' "ELEMENT_ID" From Dual Union All
Select 501 "COMPANY_CODE", 'EL_52_E' "ELEMENT_ID" From Dual Union All
Select 505 "COMPANY_CODE", 'EL_53_E' "ELEMENT_ID" From Dual Union All
Select 501 "COMPANY_CODE", 'EL_54_E' "ELEMENT_ID" From Dual
)
... then (if I got it right) the main SQL (your Cursor) could be like this:
SELECT
a.INSTANCE_NAME "INSTANCE_NAME",
c.COMPANY_CODE "COMPANY_CODE",
e.ELEMENT_ID "ELEMENT_ID"
FROM
active_t a
INNER JOIN
company_t c ON(c.INSTANCE_NAME = a.INSTANCE_NAME)
INNER JOIN
element_t e ON(e.COMPANY_CODE = c.COMPANY_CODE)
WHERE
a.IS_ACTIVE = 1
resulting as
-- INSTANCE_NAME COMPANY_CODE ELEMENT_ID
-- ------------- ------------ ----------
-- B 202 EL_21_B
-- B 203 EL_22_B
-- C 301 EL_31_C
-- E 501 EL_51_E
-- E 501 EL_52_E
-- E 501 EL_54_E
All of this is just for generating some sample data to work with. Now, if you want to use the data in a PL/SQL block then you can do it like below:
SET SERVEROUTPUT ON
Declare
CURSOR cur IS
SELECT
a.INSTANCE_NAME "INSTANCE_NAME",
c.COMPANY_CODE "COMPANY_CODE",
e.ELEMENT_ID "ELEMENT_ID"
FROM
active_t a
INNER JOIN
company_t c ON(c.INSTANCE_NAME = a.INSTANCE_NAME)
INNER JOIN
element_t e ON(e.COMPANY_CODE = c.COMPANY_CODE)
WHERE
a.IS_ACTIVE = 1;
curSet cur%ROWTYPE;
Begin
OPEN cur; -- Open cursor
LOOP
FETCH cur INTO curSet; -- fetch cursor row by row into curSet variable
EXIT WHEN cur%NOTFOUND; -- exit Loop when there are no rows left in the cursor
-- Here you can do whatever you want with fetched cursor row
-- I will just print the data out so you can see how to refference values from curSet variable
DBMS_OUTPUT.PUT_LINE('Instance: ' || curSet.INSTANCE_NAME || ' Company Code: ' || curSet.COMPANY_CODE || ' Element: ' || curSet.ELEMENT_ID);
END LOOP;
CLOSE cur; -- close cursor
End;
DBMS_OUTPUT is:
-- anonymous block completed
-- Instance: B Company Code: 202 Element: EL_21_B
-- Instance: B Company Code: 203 Element: EL_22_B
-- Instance: C Company Code: 301 Element: EL_31_C
-- Instance: E Company Code: 501 Element: EL_51_E
-- Instance: E Company Code: 501 Element: EL_52_E
-- Instance: E Company Code: 501 Element: EL_54_E
Regards...

Related

ORA-01722: Invalid number for Fr Locale for Oracle 11g

I have a view named My_View which contains one varchar column having numeric decimal data.
While,select calculating avg in am getting error
ORA-01722: Invalid number for Fr Locale
Here is my oracle query which i tried but getting the error:
select (AVG(MY_COLUMN))
from MY_TABLE;
select TO_NUMBER((AVG(MY_COLUMN)), '90.9999', 'NLS_NUMERIC_CHARACTERS='',.''')
from MY_TABLE
GROUP BY MY_COLUMN;
How to get rid of this error?
Starting with Oracle 12.2, you can use the on conversion error clause of to_number() to return a default value when the conversion fails. This is handy for your use case: you can return null on conversion error, which aggregate function avg() will happily ignore.
select avg(
to_number(
my_column default null on conversion error,
'9999d9999',
'nls_numeric_characters = ''.,'''
)
) my_avg
from my_table;
Problem seems to be in data that isn't "numeric" (why do you keep numbers in VARCHAR2 columns)? For example, it contains '123A56'. What is AVG of that value?
A simple option is to use REGEXP_LIKE and perform numeric operations only on "valid" values. For example:
SQL> with test (col) as
2 (select '1234.56' from dual union all -- valid
3 select '131' from dual union all -- valid
4 select 'ABC' from dual union all -- invalid
5 select 'xy.23' from dual union all -- invalid
6 select '.3598' from dual union all -- invalid
7 select '12.34.56'from dual -- invalid
8 )
9 select col,
10 to_number(col, '9999D9999', 'nls_numeric_characters = ''.,''') col_as_num
11 from test
12 where regexp_like(col, '^\d+\.?\d+$');
COL COL_AS_NUM
-------- ----------
1234.56 1234,56
131 131
SQL>
Now you can AVG such values:
SQL> with test (col) as
2 (select '1234.56' from dual union all -- valid
3 select '131' from dual union all -- valid
4 select 'ABC' from dual union all -- invalid
5 select 'xy.23' from dual union all -- invalid
6 select '.3598' from dual union all -- invalid
7 select '12.34.56'from dual -- invalid
8 )
9 select avg(to_number(col, '9999D9999', 'nls_numeric_characters = ''.,''')) result
10 from test
11 where regexp_like(col, '^\d+\.?\d+$');
RESULT
----------
682,78
SQL>

Merge results from two tables through 1 common column

We have some warehouses which we do inventory on a regular basis.
I need to make a report (the goal is to use Microsoft PowerBi) to show the discrepancies between different inventories for the same warehouse (location), but the only common column is the warehouse number.
Product column is dynamically: it may appear on an inventory and not on another (and that is exactly what we need to know)
Each inventory is on a different table, like this:
TABLE A
LOCATION PRODUCTS QTY
WH 1 PRODUCT NO. 1 10
WH 1 PRODUCT NO. 2 100
WH 1 PRODUCT NO. 333 5
WH 2 PRODUCT NO. YYY 22
TABLE B
LOCATION PRODUCTS QTY
WH 1 PRODUCT NO. 1 10
WH 1 PRODUCT NO. 2 100
WH 1 PRODUCT NO. 333 5
WH 1 PRODUCT NO. XXX 77
WH 2 PRODUCT NO. YYY 45
WH 1 PRODUCT NO. YYY 555
WH 2 PRODUCT NO. 1 14
Expected output should be like this:
Location products QTY A QTY B
WH 1 PRODUCT NO. 1 10 10
WH 1 PRODUCT NO. 2 100 100
WH 1 PRODUCT NO. 333 5 5
WH 1 PRODUCT NO. XXX - 77
WH 1 PRODUCT NO. YYY - 555
WH 2 PRODUCT NO. 1 - 14
WH 2 PRODUCT NO. YYY 22 45
I´ve read about FULL OUTER JOIN and also PIVOT TABLES, but I was unable to fully understand and use them (and I´m also not sure they are the goal here).
SQL version is Microsoft SQL Server 2016 Standard
One option is a Union ALL in concert with a PIVOT or Conditional Aggregation
Example
Select *
From (
Select Location
,Products
,Qty
,Col = 'Qty A'
From Table A
Union All
Select Location
,Products
,Qty
,Col = 'Qty B'
From Table B
) src
Pivot (sum(Qty) for Col in ([Qty A],[Qty B] ) ) pvt
Isn't this a simple full join? Am I missing something? Pivot seems unnecessary here.
select isnull(A.location,B.location) location, isnull(A.products, B.products) products,
A.QTY A_QTY, B.QTY B_QTY
from tableA A
full outer join tableB B
on A.location = B.location
and A.products = B.products
A_QTY or B_QTY will be null if it's in one table but not the other.
Try this:
;with tableA (LOCATION, PRODUCTS, QTY)
as
(
select 'WH 1','PRODUCT NO. 1',10
union all
select 'WH 1','PRODUCT NO. 2',100
union all
select 'WH 1','PRODUCT NO. 333',5
union all
select 'WH 2','PRODUCT NO. YYY',22
)
, tableB (LOCATION, PRODUCTS, QTY)
as
(
select 'WH 1','PRODUCT NO. 1',10
union all
select 'WH 1','PRODUCT NO. 2',100
union all
select 'WH 1','PRODUCT NO. 333',5
union all
select 'WH 1','PRODUCT NO. XXX',77
union all
select 'WH 2','PRODUCT NO. YYY',45
union all
select 'WH 1','PRODUCT NO. YYY',555
union all
select 'WH 2','PRODUCT NO. 1',14
)
select
LOCATION
, PRODUCTS
, isnull(QtyA,0) [Qty A]
, isnull(QtyB,0) [Qty B]
from
(
select
LOCATION
, PRODUCTS
, QTY
, 'QtyA' ColumnQty
from tableA
union all
select
LOCATION
, PRODUCTS
, QTY
, 'QtyB' ColumnQty
from tableB
) source
pivot
(
sum(QTY) for ColumnQty in ([QtyA],[QtyB])
) pvt
I've tested it and it works as can be seen here.

Cursor with where condition

I have a cursor which contains bills of all the persons grouped by their family name. Now, I want to check dynamically that which family name has outstanding bill against it. For eg. there is adams and perry family and many more. The cursor has values for all the members of the families with one column being the family name. Now I check for the outstanding amount, I find their is one in adams family. Now, I want my cursor to display all the members of adams family so I can distribute the bill to the family's other members.
CURSOR individual_cur is
select name,family_name, bill from table1;
CURSOR family_cur is
select family_name from table1;
for temp in family_cur loop
select sum(bill) into extra_bill where family_name is temp.family_name;
if extra_bill <>0 then
-- Now here I want
for temp1 in individual_cur loop *where family_name is temp.family_name*
-- How to do this.
end loop;
Here's an example
declare
-- add family name as parameter
-- and use in where clause
cursor individual_cur(p_family_name table1.family_name%type) is
select name
,family_name
,bill
from table1
where family_name = p_family_name;
--group by family
-- and filter on sum(bill)<>0
cursor family_cur is
select family_name
from table1
group by family_name
having sum(bill)<>0;
begin
for temp in family_cur
loop
-- select individuals from this family
for temp1 in individual_cur(temp.family_name)
loop
-- do something
end loop;
end loop;
end;
Or in just one query
select name
,family_name
,bill
from table1
where family_name in (select family_name
from table1
group by family_name
having sum(bill)<>0)
I assume what you're trying to do is average out the bill across all the family members? If so, then this can be done in a single query:
WITH table1 AS (SELECT 'bob' NAME, 'jones' family_name, 0 bill FROM dual UNION ALL
SELECT 'jane', 'jones', 100 bill FROM dual UNION ALL
SELECT 'fred', 'jones', 50 bill FROM dual UNION ALL
SELECT 'bill', 'smith', 0 bill FROM dual UNION ALL
SELECT 'brenda', 'smith', 60 bill FROM dual UNION ALL
SELECT 'jill', 'smith', 0 bill FROM dual UNION ALL
SELECT 'bob', 'ingles', 0 bill FROM dual UNION ALL
SELECT 'carol', 'ingles', 0 bill FROM dual UNION ALL
SELECT 'rob', 'carolgees', 200 bill FROM dual)
-- end of mimicking your table1 with sample data in it - you wouldn't need this
-- see the SQL below
SELECT NAME,
family_name,
bill,
AVG(bill) OVER (PARTITION BY family_name) split_bill
FROM table1;
NAME FAMILY_NAME BILL SPLIT_BILL
------ ----------- ---------- ----------
rob carolgees 200 200
carol ingles 0 0
bob ingles 0 0
fred jones 50 50
bob jones 0 50
jane jones 100 50
jill smith 0 20
bill smith 0 20
brenda smith 60 20
This uses the AVG() analytic function to split the bill across all the members in the family, without compressing the rows like the AVG() aggregate function would do.
If you then needed to update table1 to reflect the shared bills, you could then plug the above query into a MERGE statement, something like:
merge into table1 tgt
using (select name,
family_name,
bill,
avg(bill) over (partition by family_name) split_bill
from table1) src
on (tgt.name = src.name and tgt.family_name = src.family_name)
when matched then
update set tgt.bill = src.split_bill
where tgt.bill != src.split_bill;

SQL Server 2008 - order by strings with number numerically

I have following values in my table:
ABC
ABC1
ABC2
ABC3 and so on...
ABC11
ABC12
ABC13 and so on..
ABC20
ABC21
ABC22 and so on..
So basically what I have is any string value (not always ABC, any string value) that can either be followed by the number or it may just be a string without the number.
When I do select * from table order by my column asc I get following results:
ABC
ABC1
ABC11
ABC12
ABC13
ABC2
ABC20
ABC21
ABC22
ABC3
ABC31
ABC32
I need it sorted numerically:
ABC
ABC1
ABC2
ABC3
ABC11
ABC12
ABC13
ABC20
ABC21
ABC22
ABC31
ABC32
How can this be accomplished?
You can do it using PATINDEX() function like below :
select * from Test
order by CAST(SUBSTRING(Name + '0', PATINDEX('%[0-9]%', Name + '0'), LEN(Name + '0')) AS INT)
SQL Fiddle Demo
If you have numbers in middle of the string then you need to create small user defined function to get number from string and sort data based on that number like below :
CREATE FUNCTION dbo.fnGetNumberFromString (#strInput VARCHAR(255))
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #intNumber int
SET #intNumber = PATINDEX('%[^0-9]%', #strInput)
WHILE #intNumber > 0
BEGIN
SET #strInput = STUFF(#strInput, #intNumber, 1, '')
SET #intNumber = PATINDEX('%[^0-9]%', #strInput)
END
RETURN ISNULL(#strInput,0)
END
GO
You can sort data by :
select Name from Test order by dbo.fnGetNumberFromString(Name), Name
(based on answers from #shenhengbin and #EchO to this question)
The following is what I call a "clean hack". Assuming you are ordering on column Col1:
ORDER BY LEN(Col1), Col1
It is a hack, although I'd personally feel proud using it.
In order by statement, prepend enough zeros with when value contains any number in it to make all alphanumerica value same length
SELECT ColName
FROM TableName
ORDER BY
CASE WHEN ColName like '%[0-9]%'
THEN Replicate('0', 100 - Len(ColName)) + ColName
ELSE ColName END
You could remove the first three characters and cast the rest to int
SELECT Value,
Num=CAST(RIGHT(Value, LEN(Value) - 3) AS int)
FROM dbo.TableName
ORDER BY Num
Demo
You could adapt the function RemoveNonAlphaCharacters in this answer to filter out everything except numbers, and then use an ORDER BY using that function.
Using Standard SQL ONLY this query demonstrates how you can ODER BY either the numbers found to be contained at the Beginning or the End of the string. The query also shows how you could then look at the remaining 'inner' portion of the string to see if there are any numbers contained there. Knowing if there are numbers contained within the string could be useful if further processing is necessary.
WITH stringNumberData AS
( /* Build up Fake data with Numbers at the Beginning, End and Middle of the string */
SELECT 1 AS uniqueKey, 'ABC' AS NumberFromString UNION ALL
SELECT 2 AS uniqueKey, 'ABC1' AS NumberFromString UNION ALL
SELECT 3 AS uniqueKey, 'ABC2' AS NumberFromString UNION ALL
SELECT 4 AS uniqueKey, 'ABC3' AS NumberFromString UNION ALL
SELECT 5 AS uniqueKey, 'ABC10' AS NumberFromString UNION ALL
SELECT 6 AS uniqueKey, 'ABC11' AS NumberFromString UNION ALL
SELECT 7 AS uniqueKey, 'ABC12' AS NumberFromString UNION ALL
SELECT 8 AS uniqueKey, 'ABC20' AS NumberFromString UNION ALL
SELECT 9 AS uniqueKey, 'ABC21' AS NumberFromString UNION ALL
SELECT 10 AS uniqueKey, 'ABC22' AS NumberFromString UNION ALL
SELECT 11 AS uniqueKey, 'ABC30' AS NumberFromString UNION ALL
SELECT 12 AS uniqueKey, 'ABC31' AS NumberFromString UNION ALL
SELECT 13 AS uniqueKey, 'ABC32' AS NumberFromString UNION ALL
SELECT 14 AS uniqueKey, '1ABC' AS NumberFromString UNION ALL
SELECT 15 AS uniqueKey, '2ABC' AS NumberFromString UNION ALL
SELECT 16 AS uniqueKey, '3ABC' AS NumberFromString UNION ALL
SELECT 17 AS uniqueKey, '10ABC' AS NumberFromString UNION ALL
SELECT 18 AS uniqueKey, '11BC' AS NumberFromString UNION ALL
SELECT 19 AS uniqueKey, '12ABC' AS NumberFromString UNION ALL
SELECT 20 AS uniqueKey, '10ABC18' AS NumberFromString UNION ALL
SELECT 21 AS uniqueKey, '11BC52' AS NumberFromString UNION ALL
SELECT 22 AS uniqueKey, '12ABC42' AS NumberFromString UNION ALL
SELECT 23 AS uniqueKey, 'A3BC18' AS NumberFromString UNION ALL
SELECT 24 AS uniqueKey, 'B3C52' AS NumberFromString UNION ALL
SELECT 25 AS uniqueKey, '12AB3C' AS NumberFromString UNION ALL
SELECT 26 AS uniqueKey, 'A3BC' AS NumberFromString UNION ALL
SELECT 27 AS uniqueKey, 'AB2C' AS NumberFromString UNION ALL
SELECT 28 AS uniqueKey, 'ABC85D' AS NumberFromString
)
SELECT d.uniqueKey, d.NumberFromString
/* Extract numerical values contained on the LEFT of the String by finding the index of the first non number */
, LEFT(d.NumberFromString, PATINDEX('%[^0-9]%', d.NumberFromString) -1) AS 'Left Numbers Extraction'
/* Extract numerical data contained on the RIGHT of the String */
, RIGHT(d.NumberFromString, PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) - 1 ) AS 'Right Numbers Extraction'
/* The below checks inside the Inner string to determine if numbers exists within it. Could be used for further processing if further extraction is necessary */
, PATINDEX('%[0-9]%',
SUBSTRING(d.NumberFromString /*, Start Pos, Length to Extract) */
, PATINDEX('%[^0-9]%', d.NumberFromString) /* Start Pos is first left non number */
/* The below obtains the length of the Inner String so it can be extracted */
, LEN(d.NumberFromString) - ((PATINDEX('%[^0-9]%', d.NumberFromString) -1 )) - (PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1) /* (String Length) - (LEFT Numbers) - (RIGHT Numbers) */
)) AS innerNumExists
/* The two lines below tell us if there exists a number at the Beginning and/or End of the string */
, PATINDEX('%[0-9]%', LEFT(d.NumberFromString, 1)) AS leftNumExists
, PATINDEX('%[0-9]%', RIGHT(d.NumberFromString, 1)) AS rightNumExists
/* Locates and returns the index of the very first number located in the string from Left to Right */
, PATINDEX('%[^0-9]%', d.NumberFromString) AS firstLeftNonNum_index
/* Locates and returns the index of the very first number located in the string from Right to Left */
, LEN(d.NumberFromString) - (PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1 ) AS firstRightNonNum_index
/* Get the length of the numbers existing from Right to Left up to the first non numeric character */
, PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1 AS rightStringLen
FROM stringNumberData d
ORDER BY
/* Ordering first by numbers found on the LEFT of the string */
CAST(LEFT(d.NumberFromString, PATINDEX('%[^0-9]%', d.NumberFromString) -1) AS INT )
/* Ordering second by numbers found on the RIGHT of the string */
, CAST(RIGHT(d.NumberFromString, PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) - 1 ) AS INT )
;
Here's a simple to understand example for those using SQL Server 17+.
DECLARE #Data table ( val varchar(10) );
INSERT INTO #Data VALUES
( 'ABC' ),( 'ABC1' ),( 'ABC11' ),( 'ABC12' ),( 'ABC13' ),( 'ABC2' ), ( 'B1C' ),
( 'ABC20' ),( 'ABC21' ),( 'ABC22' ),( 'ABC3' ),( 'ABC31' ),( 'ABC32' );
SELECT val FROM #Data AS d
CROSS APPLY (
SELECT CAST ( TRANSLATE ( d.val, 'abcdefghijklmnopqrstuvwxyz', ' ' ) AS int ) AS Num
) AS x
ORDER BY
LEFT ( val, 1 ), Num;
Returns
+-------+
| val |
+-------+
| ABC |
| ABC1 |
| ABC2 |
| ABC3 |
| ABC11 |
| ABC12 |
| ABC13 |
| ABC20 |
| ABC21 |
| ABC22 |
| ABC31 |
| ABC32 |
| B1C |
+-------+
SQL Server's TRANSLATE takes three parameters: inputString, characters, translations.
The inputString in your case is your column name.
The characters are the values you're looking to replace, in this case the alphabet.
The translations are the values to replace with. This string must be equal in length to the characters--hence the empty string that's 26 spaces long.
And finally, using CAST ignores the spaces and allows the remaining value to be sorted as an int.
You can read about TRANSLATE here:
https://learn.microsoft.com/en-us/sql/t-sql/functions/translate-transact-sql?view=sql-server-ver15

Retrieve only distinct combinations

I'm having column as Node varchar(25) in MS-SQL Server.
The possible values are
node
-----
D-C
C-B
B-A
B-C
B-E
C-A
A-B
A-C
C-D
etc.
I want to retrieve the the distinct combinations from it.
E.g.:
node
----
D-C
C-B
B-A
B-E
C-A
Please tell the SQL for this.
You have two pieces of data pressed into one column. This is not ideal. So my solution first has to correct this:
SQL> create table mytable (node)
2 as
3 select 'D-C' from dual union all
4 select 'C-B' from dual union all
5 select 'B-A' from dual union all
6 select 'B-C' from dual union all
7 select 'B-E' from dual union all
8 select 'C-A' from dual union all
9 select 'A-B' from dual union all
10 select 'A-C' from dual union all
11 select 'C-D' from dual
12 /
Table created.
SQL> with structured_data as
2 ( select regexp_substr(node,'[^-]+',1,1) startnode
3 , regexp_substr(node,'[^-]+',1,2) endnode
4 from mytable
5 )
6 select distinct least(startnode,endnode) startnode
7 , greatest(startnode,endnode) endnode
8 from structured_data
9 /
STARTNODE ENDNODE
--------- -------
B E
A C
A B
C D
B C
5 rows selected.
select distinct(Node) from YOUR_TABLE;
Here is a SQLfiddle with examples : http://www.sqlfiddle.com/#!4/76484/2
I answered for oracle since the question is tagged oracle. For MS-Server, it's probably the same thing though...
Another approach. Very similar to what Rob van Wijk has posted in terms of using greatest() and least() functions, but without invocation of regular expression functions. We can calculate greatest and least of column value and its revers value - value returned by the reverse() function:
Note: reverse() function is undocumented function. Do not use it in the the production application(s).
with t1(col) as(
select 'D-C' from dual union all
select 'C-B' from dual union all
select 'B-A' from dual union all
select 'B-C' from dual union all
select 'B-E' from dual union all
select 'C-A' from dual union all
select 'A-B' from dual union all
select 'A-C' from dual union all
select 'D-C' from dual
)
select res_1 /* as well/or we can choose res_2 */
from (select distinct
greatest(col, reverse(col)) as res_1
, least(col, reverse(col)) as res_2
from t1)
Result:
RES_1
-----
D-C
B-A
C-A
C-B
E-B
Or #2
select col
from ( select col
, row_number() over(partition by greatest(col, reverse(col))
, least(col, reverse(col))
order by col) as rn
from t1
)
where rn = 1
Result:
COL
---
A-B
A-C
B-C
D-C
B-E

Resources