Oracle order by varchar2 with numeric values in it - oracle

I have an issue where a Oracle DB column(say 'REF_NO') is VARCHAR2 and carries values similar to the ones below
If I do an ORDER BY REF_NO I get this:
LET-2-1
LET-2-10
LET-2-11
LET-2-2
LET-2-3
Which makes sense because the values are being treated as characters. I have been asked to change this so that the returned results are ordered like this:
LET-2-1
LET-2-2
LET-2-3
LET-2-10
LET-2-11
I cannot guarantee the format of these values either so I cannot really see how I can use regex or sub-string as it's a completely free text entry for users to enter values. The example above just happens to be what the requested data looks like. Other data could be completely different.
I cannot see how this is possible, so was hoping for some suggestions.
Additional information
To add to the complexity, here are some more examples from other customers:
Customer 1: OB 12, WE-11, WAN-001
Customer 2: P4, D1, W9
Customer 3: NTT-33A, RLC-33L, ARR-129B

Here are the steps
create table test01 (c varchar2(30));
insert into test01 values ('LET-2-1');
insert into test01 values ('LET-2-10');
insert into test01 values ('LET-2-2');
Query
select * from test01
order by to_number(SUBSTR(C,INSTR(C,'-',1)+1,INSTR(C,'-',1,2)-INSTR(C,'-',1)-1)),
to_number(SUBSTR(C,INSTR(C,'-',-1)+1));
Assuming all the values are having structure of -- - in the above query I try to extract numbers and converted to number in order by clause.

select * from TABLE
ORDER BY LENGTH(REF_NO), REF_NO
first you need to order by length and then order by REF_NO

Related

find a best way to traverse oracle table

I have an oracle table. Table's DDL is (not have the primary key)
create table CLIENT_ACCOUNT
(
CLIENT_ID VARCHAR2(18) default ' ' not null,
ACCOUNT_ID VARCHAR2(18) default ' ' not null,
......
)
create unique index UK_ACCOUNT
on CLIENT_ACCOUNT (CLIENT_ID, ACCOUNT_ID)
Then, the data's scale is very huge, maybe 100M records. I want to traverse this whole table's data with batch.
Now, I use the table's index to batch traverse. But I have some oracle grammar problems.
# I want to use this SQL, but grammar error.
# try to use b-tree's index to locate start position, but not work
select * from CLIENT_ACCOUNT
WHERE (CLIENT_ID, ACCOUNT_ID) > (1,2)
AND ROWNUM < 1000
ORDER BY CLIENT_ID, ACCOUNT_ID
Has the fastest way to batch touch table data?
Wild guess:
select * from CLIENT_ACCOUNT
WHERE CLIENT_ID > '1'
and ACCOUNT_ID > '2'
AND ROWNUM < 1000;
It would at least compile, although whether it correctly implements your business logic is a different matter. Note that I have cast your filter criteria to strings. This is because your columns have a string datatype and you are defaulting them to spaces, so there's a high probability those columns contain non-numeric values.
If this doesn't solve your problem, please edit your question with more details; sample input data and expected output is always helpful in these situations.
Your data model seems odd.
Your columns are defined as varchar2. So why is your criteria numeric?
Also, why do you default the key columns to space? It would be better to leave unpopulated values as null. (To be clear, NULL is not a good thing in an indexed column, it's just better than a space.)

Inserting Record Type

I have a source table, and a history table. They are identical, except the history table has a notes column as the last column.
The table is large, 55 columns, which I do not want to list all the columns. We do not want to use a trigger on this, but just create the history entry in the code itself.
If I simply do an INSERT INTO <history> SELECT * FROM <source> WHERE...... I will get "not enough values".
I'm hoping to do something of this nature: (Note this is just an anonymous block)
DECLARE
v_old_rec company_table%ROWTYPE;
BEGIN
SELECT * INTO v_old_rec
FROM company_table
WHERE company_id = 32789;
INSERT INTO company_table_hist
VALUES v_old_rec || ',MONTHLY UPDATE';
END;
Anything like this possible, so I do not have to list 55 columns?
Thank you.
It's quite simple actually:
INSERT INTO company_table_hist
(SELECT t.*, MONTHLY_UPD
FROM company_table t
WHERE company_id = 32789);
Of course monthly_upd must be bound somehow

SQL Table Column that uses input from Another Column in Same Table

I want to have a column in table which calculates the value depending on the other column in the same table.
For example,
I have "Validity" Table with Columns "DateManufactured", "DateExpires"
The Date Expires column must calculate value suppose adding 30 days for Datemanufactured.
How Can we do this in Visual Studio2010->DataSet Design-> DataTable Column-> Properties->Expression
See relevant Image here
What could be the possible expression for this in terms of SQL Server Expressions?
Please Suggest optimal Solution.
Thanks in advance.
I believe you are looking for DateAdd
SELECT DATEADD(day, 30, '15 Dec 1988')
select dateadd(day,30,getdate())
this will take the current date(getdate())
add 30 days to it.
I would suggest creating a stored procedure to insert your data into your table with parameters of the values that needs to be inserted. you can then do your calculation based on your date parameter. Example:
Create Procedure InsertValidity
(
#City varchar(20),
#Area varchar(20),
...
#DatePosted datetime,
...
#UserID
)
as
declare #DurationFrom datetime
set #DurationFrom = (select DATEADD(dd,30,#DatePosted)
insert into Validity (City, Area, ..., DatePosted, ... UserID)
values(#City, #Area, ..., #DatePosted,...#DurationFrom,...#UserID)
This should solve your problem. Just complete the script by replacing the ... with your other data then execute the stored procedure in your application.

Oracle: use index for searching null values

I've done some search but I prefer something like an hint or similar
http://www.dba-oracle.com/oracle_tips_null_idx.htm
http://www.oracloid.com/2006/05/using-index-for-is-null/
What about a functional index using NVL2, like;
CREATE TABLE foo (bar INTEGER);
INSERT INTO foo VALUES (1);
INSERT INTO foo VALUES (NULL);
CREATE INDEX baz ON foo (NVL2(bar,0,1));
and then;
DELETE plan_table;
EXPLAIN PLAN FOR SELECT * FROM foo WHERE NVL2(bar,0,1) = 1;
SELECT operation, object_name FROM plan_table;
should give you
OPERATION OBJECT_NAME
---------------- -----------
SELECT STATEMENT
TABLE ACCESS FOO
INDEX BAZ << yep
If you're asking, "How can I create an index that would allow it to be used when searching for NULL values on a particular field", my suggestion is to create an index on the field you're interested in PLUS the primary key field(s). Thus, if you've got a table called A_TABLE, with field VAL that you want to search for NULLs, and a primary key named PK, I'd create an index on (VAL, PK).
Share and enjoy.
I'm going to "answer" the non-question above.
The articles you link to are kinda right - Oracle's b-tree indexes will not capture when the leaf nodes are null. Take this example:
CREATE TABLE MYTABLE (
ID NUMBER(8) NOT NULL,
DAT VARCHAR2(100)
);
CREATE INDEX MYTABLE_IDX_1 ON MYTABLE (DAT);
/* Perform inserts into MYTABLE where some DAT are null */
SELECT COUNT(*) FROM MYTABLE WHERE DAT IS NULL;
The ending SELECT will not be able to use the index, because the leafs (right-most column) will not capture the nulls. Burleson's solution is stupid, because now you have to use a NVL in all your queries and have compromised the data in the tables. Gorbachev's method includes a known NOT NULL column for the leaves of the b-tree, but this expands the index for no reason. Maybe in his case the index made sense that way for tuning other queries, but if all you want to do is find the NULLs then the easiest solution is to make the leaf a constant.
CREATE INDEX MYTABLE_IDX_1 ON MYTABLE (DAT, 1);
Now, the leaves are all the constant (1), and by default the nulls will all be together (either at the top or bottom of the index, but it doesn't really matter as Oracle can use the index forwards or backwards). There is a slight storage penalty for that constant, but a single number is smaller than most other data fields in a typical table. Now the database can use the index when querying for nulls...if the optimizer finds that the best way to get the data.

SQL Server 2008 search for date

I need to search rows entered on a specific date.
However the datatype of column I need to search on is datetime, and the datatype of argument is Date.
I can use the the query like
Select result
from table
where
convert(date, Mycolumn) = #selectedDate
but this would affect the SARGability of the query and will not use indexes created on mycolumn.
I was trying to use the following query:
Select result
from table
where
Mycolumn
BETWEEN #selectedDate AND Dateadd(s, -1, Dateadd(D, 1, #selectedDate))
However this does not work since the #selectedDate is Date type and a second can't be added or removed.
Can someone help me with a working query?
Thanks.
It is my understanding that using:
convert(date, Mycolumn) = #selectedDate
is SARGable. It will use the index on Mycolumn (if one exists). This can easily be confirmed by using the execution plan.
Select result
from table
where
Mycolumn >= #selectedDate
AND Mycolumn < Dateadd(D, 1, #selectedDate)
If you need to do these searches a lot, you could add a computed, persisted column that does the conversion to DATE, put an index on it and then search on that column
ALTER TABLE dbo.YourTable
ADD DateOnly AS CAST(MyColumn AS DATE) PERSISTED
Since it's persisted, it's (re-)calculated only when the MyColumn value changes, e.g. it's not a "hidden" call to a stored function. Since it's persisted, it can also be indexed and used just like any other regular column:
CREATE NONCLUSTERED INDEX IX01_YourTable_DateOnly ON dbo.YourTable(DateOnly)
and then do:
SELECT result FROM dbo.YourTable WHERE DateOnly = #SelectedDate
Since that additional info is stored in the table, you'll be using a bit more storage - so you're doing the classic "space vs. speed" trade-off; you need a bit more space, but you get more speed out of it.

Resources