In a view i under the SOURCE column I have the following values.
SRC - TERM - randomtext - LOCATION (Just a FYI on the format of the source column)
ABC DE RANDOMJIBBERISH MORE RANDOMJIBBERISH FORWARD
ARY HES RANDOMJIBBERISH MORE RANDOMJIBBERISH BACKWARD
IGHE UER RANDOMJIBBERISH MORE RANDOMJIBBERISH LEFT
Now I have a query that needs to lookup on that view BASED on the source. This one works perfectly fine.
SELECT
t.DATE_, t.PX_LAST
FROM
THIS.TABLE_NEW t
WHERE
t.DATE_ >= '2003-03-02'
AND t.DATE_ <= '2013-03-02'
AND t.SOURCE LIKE 'ABC DE % FORWARD' --Where the magic happens
AND t.SOURCE LIKE '%'||'1M'||'%'
AND t.PX_LAST is NOT NULL
ORDER BY
t.DATE_ ASC;
Now, the issue is, when I try to implement this in a stored procedure, I will need to insert the percent sign in the parameters I get. This doesn't work, particularly the part where it looks for the source using the inSource
PROCEDURE Get_It
(
inSource VARCHAR2,
inStartDate DATE,
inEndDate DATE,
inLength VARCHAR2,
inRC1 OUT SYS_REFCURSOR
) AS
BEGIN
OPEN inRC1 FOR
SELECT t.DATE_, t.PX_LAST
FROM THIS.TABLE_NEW t WHERE
t.DATE_ >= inStartDate
AND t.DATE_ <= inEndDate
AND t.SOURCE LIKE inSource --Where the magic needs to happen
AND t.SOURCE LIKE '%'||length||'%'
AND t.PX_LAST is NOT NULL
ORDER BY t.DATE_ ASC;
END GET_IT;
So basically I need to insert a percent sign in the MIDDLE of the string (inSource), between the last and second-last word, at all times. I was able to do it in the query because I can manually put it in the string, but in the actual stored procedure I don't know how I can manipulate the string.
Assuming you want to pass in a value of inSource like 'ABC DE FORWARD' and have the procedure translate that to 'ABC DE % FORWARD' - with the % always between the second and third word - then you can mess around with chopping up and reconstructing the string, or you can do a simple regexp_replace():
AND t.SOURCE LIKE regexp_replace(inSource, ' ', ' % ', 1, 2)
AND t.SOURCE LIKE '%'||inLength||'%'
This replaces a space with a space-padded percentage sign, starting as position 1 (i.e. the start of the string) and only applying to the second instance - so it skips over the first space and only affects the one between the second and third words.
To demonstrate this:
select regexp_replace('ABC DE FORWARD', ' ', ' % ', 1, 2)
from dual;
REGEXP_REPLACE('
----------------
ABC DE % FORWARD
Looks to me that your query is fine but your inLength parameter is not being used correctly in the cursor definition.
Based on your edit clarification there are two possible solutions.
First option: Pass inSource as two varchars which would be all of the string up until the space and then the last word
PROCEDURE Get_It
(
inSourceFirst VARCHAR2,
inSourceLast VARCHAR2,
inStartDate DATE,
inEndDate DATE,
inLength VARCHAR2,
inRC1 OUT SYS_REFCURSOR
) AS
BEGIN
OPEN inRC1 FOR
SELECT t.DATE_, t.PX_LAST
FROM THIS.TABLE_NEW t WHERE
t.DATE_ >= inStartDate
AND t.DATE_ <= inEndDate
AND t.SOURCE LIKE inSourceFirst || '%' || inSourceLast
AND t.SOURCE LIKE '%'|| inLength ||'%'
AND t.PX_LAST is NOT NULL
ORDER BY t.DATE_ ASC;
END GET_IT;
Second option: Use regexp_substr() function More here. Your inSource string would then be replace by regexp_replace with two regexp_substr function call grabbing what is before and after the space with the & character appended in between. Something like...
regexp_substr('SPACE MAN BEAR PIG DOG CAT','[^ ]+{4}',1,5) || ' % ' || regexp_substr('SPACE MAN BEAR PIG DOG CAT','[^ ]+',1,6)
Related
I'm trying to make procedure to select weekly music rank based on hits and likes.
create or replace procedure select_rank(
v_title IN music.title%TYPE,
v_release_date IN music.release_date%TYPE,
v_hit IN music.hit%TYPE
) is
v_cnt number := 0;
BEGIN
select music.title, music.release_date, count(melon_user.user_idx) as likes, music.hit
into v_rank, v_title, v_cnt, v_release_date, v_hit
from melon_user, user_like_music, music
where melon_user.user_idx = user_like_music.user_idx
and user_like_music.music_idx = music.music_idx
group by music.title, music.hit, music.release_date
order by count(melon_user.user_idx) desc, music.hit desc;
DBMS_OUTPUT.PUT_LINE('v_title: ' || v_title);
DBMS_OUTPUT.PUT_LINE('v_cnt: ' || v_cnt);
DBMS_OUTPUT.PUT_LINE('v_release_date: ' || v_release_date);
DBMS_OUTPUT.PUT_LINE('v_hit: ' || v_hit);
END select_rank;
/
But I keep getting error says
PLS-00403: expression 'V_RELEASE_DATE' cannot be used as an
INTO-target of a SELECT/FETCH statement
PLS-00403: expression 'V_RELEASE_DATE' cannot be used as an
INTO-target of a SELECT/FETCH statement
PLS-00403: expression 'V_RELEASE_DATE' cannot be used as an
INTO-target of a SELECT/FETCH statement
how do I fix this?
In the procedure's signature you have:
v_release_date IN music.release_date%TYPE,
v_release_date is an IN parameter and will be read only. If you want to assign values to it wthin the procedure and return them then make it an OUT (or IN OUT) parameter.
However, you have also:
Not declared the v_rank variable.
Your query appears likely to return multiple rows - SELECT .. INTO .. will only work for queries that return a single row. If this is the case then you either want to use BULK COLLECT INTO or return a cursor.
Is there a fairly simple way to take an input parameter containing a comma seperated list of prefixes and return a cursor based on a select statement that uses these?
i.e. (Pseudocode)
PROCEDURE get_by_prefix(p_list_of_prefixes IN varchar2, r_csr OUT SYS_REFCURSOR)
IS
BEGIN
OPEN r_csr FOR
SELECT * FROM my_table where some_column LIKE (the_individual_fields_from p_list_of_prefixes ||'%')
END
I've tried various combinations, and now have two problems - coercing the input into a suitable table (I think it needs to go into a table type rather than a VARCHAR2_TABLE), and secondly getting the like clause to be effectively a SELECT from an internal 'pseudotable'...
EDIT: It seems that people are suggesting ways to use 'IN' with a set of potential values - whereas Im looking at using LIKE. I could use a similar technique - building up dynamic SQL, but was wondering if there isnt a more elegant way...
PL/SQL has no concept of a comma-separated list and no built-in splitter as in Perl etc, so you'll have to use one of the hand-rolled methods such as this one:
https://stewashton.wordpress.com/2016/08/01/splitting-strings-surprise
(Other methods are available.) Then it's just a matter of either populating a collection in one step and using it in the next, or else combining the two as something like this:
declare
p_list_of_prefixes varchar2(100) := 'John,Jim,Jules,Janice,Jenny';
begin
open :refcur for
with params as
( select x.firstname
from xmltable(
'ora:tokenize($X, "\,")'
passing p_list_of_prefixes as x
columns firstname varchar2(4000) path '.'
) x
)
, people as
( select 'Dave Clark' as fullname from dual union all
select 'Jim Potter' from dual union all
select 'Jenny Jones' from dual
)
select x.firstname, p.fullname
from params x
left join people p on p.fullname like x.firstname || '%';
end;
Output:
FIRSTNAME FULLNAME
-------------- -----------
John
Jim Jim Potter
Jules
Janice
Jenny Jenny Jones
Using LIKE the way you want is easy, but it is the wrong solution. (See my Comment under the original post).
Anyway - if by order of your superiors, or some other semi-legitimate reason, you must use a LIKE condition, it should look something like this:
... where ',' || p_list_of_whatever || ',' like '%,' || some_column || ',%
Concatenating commas at both ends of both sides of the comparison is needed, because you don't want Jo in the column to match John in the input list. Start from there and you will see why you need the commas on the right-hand side, and then follow from there and you will see why you need them on the left also.
Do we have any method to make first letter of every word as capital letter for a column in SAP HANA?
i.e "ask question" to "Ask Question"
With HANA 2 SPS3, there is a built in function called "Initcap"
SELECT INITCAP('that''s a new function') FROM DUMMY;
That'S A New Function
There is no builtin function for that on SQL level.
You could however write a user-defined function to do just that.
Possibly this site gives you a clue? I'm not familiar with SAP HANA but possibly you can label it as title sentence and do a case normalisation on it?
In T-SQL you could do something like this:
SELECT
UPPER(LEFT(ColumnA,1))+LOWER(RIGHT(ColumnA,(LEN(ColumnA)-1)))
FROM Table1;
You tell the DBMS to make the first letter in upper case, and the rest of the string in lower case. The LEN is needed because I assume the length of the string is not static and in the way of the script it is handled dynamically. The + is CONCAT in other dialects. Regarding whitespaces in ColumnA and the capitalization of the 2nd or more word in you column, I'm trying some stuff that Michael Valentine Jones described on 1-12-2006 here, alsee see part of the code beneath. I get back to this as soon as I figured this out. I'm not sure yet how the union all select statement works. Will take some days (due to the weekend). Anyway, maybe this helps you. Change the REPLACE of ',' with ' ' or something.
-- Create temp table to test inserting values into
create table #t (num int)
-- Create a comma delimited string to test with
declare #str varchar(500)
select #str = '4,2,7,7834,45,24,45,77'
--------------------------------------------------------
---- Code to load the delimited string into a table ----
--------------------------------------------------------
-- Create insert for comma delimited values
declare #sql varchar(8000)
select #sql = 'insert into #t select '+ replace(#str,',',' union all select ')
-- Load values from comma delimited string into a table
exec ( #sql )
I found a piece of T-SQL script that creates a function 'ProperCase' that will do the trick by calling the function in your select statement. See beneath. I foun it here: http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47718 (by jsmith8858).
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
And for example if you do a SELECT dbo.ProperCase ('ask question WHy iS tHAT'), then you get 'Ask Question Why Is That' as a result.
Hopefully this gets you on your way.
I have declared a value in parameter #Data as ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1
I need to get a result as ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M.
Which means I need to swap between the values before and after =
I have the SQL Server Query for achieving this but I need Oracle query.
Declare #Data varchar(100)='ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1';
WITH
myCTE1 AS
(
SELECT CAST('<root><r>' + REPLACE(#Data,',','</r><r>') + '</r></root>' AS XML) AS parts1
)
,myCTE2 AS
(
SELECT CAST('<root><r>' + REPLACE(p1.x.value('.','varchar(max)'),'=','</r><r>') + '</r></root>' AS XML) as parts2
FROM myCTE1
CROSS APPLY parts1.nodes('/root/r') AS p1(x)
)
SELECT STUFF
(
(
SELECT ',' + parts2.value('/root[1]/r[2]','varchar(max)') + '=' + parts2.value('/root[1]/r[1]','varchar(max)')
FROM myCTE2
FOR XML PATH(''),TYPE
).value('.','varchar(max)'),1,1,'');
Expected Output if I execute the query ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M. Can anyone give an idea to do this one?
Sounds like a job for REGEXP_REPLACE:
WITH datatab as (select 'ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1' info from dual)
select info,
regexp_replace(info, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3') new_info
from datatab;
INFO NEW_INFO
--------------------------------------------- ---------------------------------------------
ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1 ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M
(as a complete aside, that's the first time I've ever written a regular expression and had it work first time. Apparently, I have gone over to the dark side... *{;-) )
ETA: If you need this in a procedure/function, you don't need to bother selecting the regular expression, you can do it in PL/SQL directly.
Here's an example of a function that returns the swapped over result:
create or replace function swap_places (p_data in varchar2)
return varchar2
is
begin
return regexp_replace(p_data, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3');
end swap_places;
/
-- example of calling the function to check the result
select swap_places('ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1') col1 from dual;
COL1
-------------------------------------------------
ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M
I am trying to write a small pl/sql script and need some help.
first, I have 2 tables called project1 , project2. both tables have a column called cust_code.
cust_code values are varchar2 type. values always begin with 1. (number 1, decimal point) and 8 digits. for example 1.10002332
when I import data into project1 table, if the last digit is 0, for example 1.22321630, the last zero is dropped and then theres only seven digits beyond the decimal point. in that case it will be 1.2232163
the script I want to write will check whether there are only 7 digits beyond the decimal point and will insert that record into the project2 table.
this is what I came up with
DECLARE
CURSOR dif IS
SELECT CUST_CODE, CUST_ID, CONTRACT_NUM, MSISDN
FROM project1
WHERE CUST_CODE IN (SELECT CUST_CODE FROM CUST_ALL);
BEGIN
FOR a in dif LOOP
IF SUBSTR(a.CUST_CODE, 10)=null
THEN
INSERT INTO project2 (cust_code)
VALUES(a.CUST_CODE);
END IF;
END LOOP;
commit;
END;
the script runs with no errors but nothing happens. on the substr function, when I chose different value than NULL, then it works. I cant figure out how to check if the 8th digit is missing.
Assaf.
Your script doesn't work because of the line:
IF SUBSTR(a.CUST_CODE, 10)=null
In plsql <something> = null will always be FALSE.
You should write:
IF SUBSTR(a.CUST_CODE, 10) IS null
But actually you don't really nead plsql, you can do it with one sql command:
INSERT INTO project2 (cust_code)
SELECT CUST_CODE, CUST_ID, CONTRACT_NUM, MSISDN
FROM project1
WHERE CUST_CODE IN (SELECT CUST_CODE FROM CUST_ALL)
AND SUBSTR(a.CUST_CODE, 10) IS null;
Try this for your condition:
IF LENGTH(a.CUST_CODE) = 10 AND SUBSTR(a.CUST_CODE,-1,10) = '0'
(check if length is 10 and also last character is 0)