Oracle procedure is griping about a select query that runs fine outside of the procedure - sql

WITH Q (L) AS
(
SELECT 1 FROM DUAL
UNION ALL
SELECT L + 1
FROM Q
WHERE L < 99
)
SELECT MIN(L)
INTO next_priority
FROM Q
LEFT JOIN gxrdird on gxrdird_priority = L
and gxrdird_pidm = aPidm_in and gxrdird_ap_ind = 'Y'
WHERE L NOT IN (select gxrdird_priority
from gxrdird where gxrdird_pidm = aPidm_in);
This query returns the results that I want when run manually. I'm trying to put it in a package procedure, but I get:
51/5 PL/SQL: SQL Statement ignored
55/22 PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got -
That corresponds to the line "SELECT L + 1" on the column L is in. Is there any way to declare L as a NUMBER specifically, inside the with clause? I've been googling for an hour, and the few examples of with clauses I can find that have parameters do not declare them as any type.
This is driving me nuts, and there's no simpler query I can come up with that gives me the correct results.
Edit, adding context:
CURSOR xxx_cur IS
SELECT ROWID, GXRDIRD_PRIORITY
FROM GXRDIRD
WHERE GXRDIRD_PIDM = aPidm_in
AND GXRDIRD_AP_IND = 'A'
AND GXRDIRD_ATYP_CODE IS NULL
AND GXRDIRD_ADDR_SEQNO IS NULL
ORDER BY GXRDIRD_PRIORITY DESC;
xxx_rec xxx_cur%ROWTYPE;
next_priority NUMBER;
BEGIN
OPEN xxx_cur;
LOOP
FETCH xxx_cur INTO xxx_rec;
EXIT WHEN xxx_cur%NOTFOUND;
-- Here we should update that particular row, but we can't just increment it.
WITH Q (L) AS
(
SELECT 1 FROM DUAL
UNION ALL
SELECT L + 1
FROM Q
WHERE L < 99
)
SELECT MIN(L)
INTO next_priority
FROM Q
LEFT JOIN gxrdird on gxrdird_priority = L and gxrdird_pidm = aPidm_in and gxrdird_ap_ind = 'Y'
WHERE L NOT IN (select gxrdird_priority from gxrdird where gxrdird_pidm = aPidm_in);
-- The above query found the lowest-numbered unused priority, and now we'll set this record to that.
UPDATE GXRDIRD SET GXRDIRD_PRIORITY = next_priority WHERE ROWID = xxx_rec.ROWID;
-- If the above record was originally 7 and the lowest was 15, now 7 is free and will be used if we loop
-- again.
DBMS_OUTPUT.PUT_LINE(OBJECT_NAME || '.P_RESEQUENCE_INACTV_ACCNTS - Changed priority ' || xxx_rec.GXRDIRD_PRIORITY || ' into ' || next_priority);
END LOOP;
Line 51: WITH Q (L) AS
Line 55: SELECT L + 1

It looks like you're trying to generate dummy rows with consecutive numbers. My preferred way of doing that would be:
WITH Q AS
(
SELECT rownum AS l
FROM dual
CONNECT BY level < 100
)
SELECT MIN(L)
INTO next_priority
FROM Q
...
Please try if this works for you.

Related

Fill in missing dates in date range from a table

table A
no date count
1 20160401 1
1 20160403 4
2 20160407 3
result
no date count
1 20160401 1
1 20160402 0
1 20160403 4
1 20160404 0
.
.
.
2 20160405 0
2 20160406 0
2 20160407 3
.
.
.
I'm using Oracle and I want to write a query that returns rows for every date within a range based on table A.
Is there some function in Oracle that can help me?
you can use the SEQUENCES.
First create a sequence
Create Sequence seq_name start with 20160401 max n;
where n is the max value till u want to display.
Then use the sql
select seq_name.next,case when seq_name.next = date then count else 0 end from tableA;
Note:- Its better not to use date,count as the column names.
Try this:
with
A as (
select 1 no, to_date('20160401', 'yyyymmdd') dat, 1 cnt from dual union all
select 1 no, to_date('20160403', 'yyyymmdd') dat, 4 cnt from dual union all
select 2 no, to_date('20160407', 'yyyymmdd') dat, 3 cnt from dual),
B as (select min(dat) mindat, max(dat) maxdat from A t),
C as (select level + mindat - 1 dat from B connect by level + mindat - 1 <= maxdat),
D as (select distinct no from A),
E as (select * from D,C)
select E.no, E.dat, nvl(cnt, 0) cnt
from E
full outer join A on A.no = E.no and A.dat = E.dat
order by 1, 2, 3
This isn't an oracle specific answer, you'll need to translate it to oracle yourself.
Create an intervals table, containing all integers from 0 to 999. Something like this:
CREATE TABLE intervals (days int);
INSERT INTO intervals (days) VALUES (0), (1);
DECLARE #rc int;
SELECT #rc = 2;
WHILE (SELECT Count(*) FROM intervals) < 1000 BEGIN
INSERT INTO intervals (days) SELECT days + #rc FROM intervals WHERE days + #rc < 1000;
SELECT #rc = #rc * 2
END;
Then all the dates in the range can be identified by adding intervals.days to the first date you've got, where the first date + intervals.days is <= the end date, and the resultant date is new. Do this by cross joining intervals to your own table. Something like (it would be in SQL, but again you'll need to translate):
SELECT DateAdd(a.date, d, i.days)
FROM (select min(date) from table_A) a, intervals I
WHERE DateAdd(a.date, d, i.days) < (select max(date) from table_A)
AND NOT EXISTS (select 1 from table_A aa where aa.date = DateAdd(a.date, d, i.days))
Hope this gives you a starting point

Check palindrome without using string functions with condition

I have a table EmployeeTable.
If I want only that records where employeename have character of 1 to 5
will be palindrome and there also condition like total character is more then 10 then 4 to 8 if character less then 7 then 2 to 5 and if character less then 5 then all char will be checked and there that are palindrome then only display.
Examples :- neen will be display
neetan not selected
kiratitamara will be selected
I try this something on string function like FOR first case like name less then 5 character long
SELECT SUBSTRING(EmployeeName,1,5),* from EmaployeeTable where
REVERSE (SUBSTRING(EmployeeName,1,5))=SUBSTRING(EmployeeName,1,5)
I want to do that without string functions,
Can anyone help me on this?
You need at least SUBSTRING(), I have a solution like this:
(In SQL Server)
DECLARE #txt varchar(max) = 'abcba'
;WITH CTE (cNo, cChar) AS (
SELECT 1, SUBSTRING(#txt, 1, 1)
UNION ALL
SELECT cNo + 1, SUBSTRING(#txt, cNo + 1, 1)
FROM CTE
WHERE SUBSTRING(#txt, cNo + 1, 1) <> ''
)
SELECT COUNT(*)
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY cNo DESC) as cRevNo
FROM CTE t1 CROSS JOIN
(SELECT Max(cNo) AS strLength FROM CTE) t2) dt
WHERE
dt.cNo <= dt.strLength / 2
AND
dt.cChar <> (SELECT dti.cChar FROM CTE dti WHERE dti.cNo = cRevNo)
The result will shows the count of differences and 0 means no differences.
Note :
Current solution is Non-Case-Sensitive for change it to a Case-Sensitive you need to check the strings in a case-sensitive collation like Latin1_General_BIN
You can use this solution as a SVF or something like that.
I dont realy understand why you dont want to use string functions in your query, but here is one solution. Compute everything beforehand:
Add Column:
ALTER TABLE EmployeeTable
ADD SubString AS
SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)
PERSISTED
GO
ALTER TABLE EmployeeTable
ADD Palindrome AS
REVERSE(SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)) PERSISTED
GO
Then your query will looks like:
SELECT * from EmaployeeTable
where Palindrome = SubString
BUT!
This is not a good idea. Please tell us, why you dont want to use string functios.
You could do it building a list of palindrome words using a recursive query that generates palindrome words till a length o n characters and then selects employees with the name matching a palindrome word. This may be a really inefficient way, but it does the trick
This is a sample query for Oracle, PostgreSQL should support this feature as well with little differences on syntax. I don't know about other RDBMS.
with EmployeeTable AS (
SELECT 'ADA' AS employeename
FROM DUAL
UNION ALL
SELECT 'IDA' AS employeename
FROM DUAL
UNION ALL
SELECT 'JACK' AS employeename
FROM DUAL
), letters as (
select chr(ascii('A') + rownum - 1) as letter
from dual
connect by ascii('A') + rownum - 1 <= ascii('Z')
), palindromes(word, len ) as (
SELECT WORD, LEN
FROM (
select CAST(NULL AS VARCHAR2(100)) as word, 0 as len
from DUAL
union all
select letter as word, 1 as len
from letters
)
union all
select l.letter||p.word||l.letter AS WORD, len + 1 AS LEN
from palindromes p
cross join letters l
where len <= 4
)
SEARCH BREADTH FIRST BY word SET order1
CYCLE word SET is_cycle TO 'Y' DEFAULT 'N'
select *
from EmployeeTable
WHERE employeename IN (
SELECT WORD
FROM palindromes
)
DECLARE #cPalindrome VARCHAR(100) = 'SUBI NO ONIBUS'
SET #cPalindrome = REPLACE(#cPalindrome, ' ', '')
;WITH tPalindromo (iNo) AS (
SELECT 1
WHERE SUBSTRING(#cPalindrome, 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome), 1)
UNION ALL
SELECT iNo + 1
FROM tPalindromo
WHERE SUBSTRING(#cPalindrome, iNo + 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome) - iNo, 1)
AND LEN(#cPalindrome) > iNo
)
SELECT IIF(MAX(iNo) = LEN(#cPalindrome), 'PALINDROME', 'NOT PALINDROME')
FROM tPalindromo

Select Random Numbers from a list

This is my query.
SELECT TOP 2 NUM
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
ORDER BY NEWID()
I'm selecting 2 random numbers from a list but I don't want that these numbers to be continuous
Sometimes the result is
NUM
----
2
3
And I don't want this
Thanks , and sorry for my English u.u
Basically the same as the 2nd approach Gordon uses except it lacks the use of the lag function and therefor will work on SQL-2008.
WITH Data AS(
SELECT *, RowNum = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM sys.objects AS O
),
r AS(
SELECT TOP 1 *, SkipRow = 0
FROM Data
WHERE Data.RowNum = 1
UNION ALL
SELECT d.*, SkipRow = CASE WHEN d.object_id BETWEEN r.object_id -2 AND r.object_id + 2 THEN 1 ELSE 0 END
FROM r
JOIN Data AS D
ON r.RowNum + 1 = D.RowNum
)
SELECT TOP 2 * FROM R
WHERE R.SkipRow = 0
One approach is to select the first number, and then select an appropriate second number:
WITH r AS (
SELECT TOP 1 num
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
ORDER BY NEWId()
)
select num
from r
union all
select top 1 q.num
from qt_pivot q join
r
on q.num not in (r.num, r.num - 1, r.num + 1)
where q.num between 1 and 45
order by newid();
Another approach (if you had SQL Server 2012+) would use lag() to remove any possibilities that do not meet the conditions:
WITH r AS (
SELECT num, row_number() over (order by newid()) as seqnum
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
)
SELECT r.num
FROM (SELECT r.*, LAG(num) OVER (ORDER BY seqnum) as prevnum
FROM r
) r
WHERE prevnum is null or
prevnum not in (num - 1, num + 1);
EDIT:
The first approach doesn't work, because SQL Server always re-evaluates CTEs, and there is not even a hint to fix this problem. Here is an alternative approach, that will ensure that values are not consecutive:
WITH r as (
SELECT (1 + checksum(newid()) * 45) as r1,
(2 + checksum(newid()) * 43) as r2
)
SELECT q.num
FROM QT_PIVOT q
WHERE q.num = r.r1 or
q.num = 1 + (r.r1 + r.r2) % 45;
This calculates a two random numbers. The first is a random position. The second is an allowable offset (hence the "2" and "43") to guarantee that the numbers are not adjacent.

How do you find a missing number in a table field starting from a parameter and incrementing sequentially?

Let's say I have an sql server table:
NumberTaken CompanyName
2 Fred 3 Fred 4 Fred 6 Fred 7 Fred 8 Fred 11 Fred
I need an efficient way to pass in a parameter [StartingNumber] and to count from [StartingNumber] sequentially until I find a number that is missing.
For example notice that 1, 5, 9 and 10 are missing from the table.
If I supplied the parameter [StartingNumber] = 1, it would check to see if 1 exists, if it does it would check to see if 2 exists and so on and so forth so 1 would be returned here.
If [StartNumber] = 6 the function would return 9.
In c# pseudo code it would basically be:
int ctr = [StartingNumber]
while([SELECT NumberTaken FROM tblNumbers Where NumberTaken = ctr] != null)
ctr++;
return ctr;
The problem with that code is that is seems really inefficient if there are thousands of numbers in the table. Also, I can write it in c# code or in a stored procedure whichever is more efficient.
Thanks for the help
A solution using JOIN:
select min(r1.NumberTaken) + 1
from MyTable r1
left outer join MyTable r2 on r2.NumberTaken = r1.NumberTaken + 1
where r1.NumberTaken >= 1 --your starting number
and r2.NumberTaken is null
I called my table Blank, and used the following:
declare #StartOffset int = 2
; With Missing as (
select #StartOffset as N where not exists(select * from Blank where ID = #StartOffset)
), Sequence as (
select #StartOffset as N from Blank where ID = #StartOffset
union all
select b.ID from Blank b inner join Sequence s on b.ID = s.N + 1
)
select COALESCE((select N from Missing),(select MAX(N)+1 from Sequence))
You basically have two cases - either your starting value is missing (so the Missing CTE will contain one row), or it's present, so you count forwards using a recursive CTE (Sequence), and take the max from that and add 1
Edit from comment. Yes, create another CTE at the top that has your filter criteria, then use that in the rest of the query:
declare #StartOffset int = 2
; With BlankFilters as (
select ID from Blank where hasEntered <> 1
), Missing as (
select #StartOffset as N where not exists(select * from BlankFilters where ID = #StartOffset)
), Sequence as (
select #StartOffset as N from BlankFilters where ID = #StartOffset
union all
select b.ID from BlankFilters b inner join Sequence s on b.ID = s.N + 1
)
select COALESCE((select N from Missing),(select MAX(N)+1 from Sequence))
this may now return a row that does exist in the table, but hasEntered=1
Tables:
create table Blank (
ID int not null,
Name varchar(20) not null
)
insert into Blank(ID,Name)
select 2 ,'Fred' union all
select 3 ,'Fred' union all
select 4 ,'Fred' union all
select 6 ,'Fred' union all
select 7 ,'Fred' union all
select 8 ,'Fred' union all
select 11 ,'Fred'
go
Try the set based approach - should be faster
select min(t1.NumberTaken)+1 as "min_missing" from t t1
where not exists (select 1 from t t2
where t1.NumberTaken = t2.NumberTaken+1)
and t1.NumberTaken > #StartingNumber
This is Sybase syntax, so massage for SQL server consumption if needed.
Create a temp table with all numbers from StartingValue to EndValue and LEFT OUTER JOIN to your data table.

MYSQL enumeration: #rownum, odd and even records

I asked a question about creating temporary/ virtual ids for query results,
mysql & php: temporary/ virtual ids for query results?
I nearly got I wanted with this link,
http://craftycodeblog.com/2010/09/13/rownum-simulation-with-mysql/
I have managed to enumerate each row,
SELECT
u.pg_id AS ID,
u.pg_url AS URL,
u.pg_title AS Title,
u.pg_content_1 AS Content,
#rownum:=#rownum+1 AS rownum
FROM (
SELECT pg_id, pg_url,pg_title,pg_content_1
FROM root_pages
WHERE root_pages.parent_id = '7'
AND root_pages.pg_id != '7'
AND root_pages.pg_cat_id = '2'
AND root_pages.pg_hide != '1'
ORDER BY pg_created DESC
) u,
(SELECT #rownum:=0) r
result,
ID URL Title Content rownum
53 a x x 1
52 b x x 2
43 c x x 3
41 d x x 4
but how can I work on it a bit further - I want to display the odd or even records only like the ones below - is it possible?
odd records,
ID URL Title Content rownum
53 a x x 1
43 c x x 3
even records,
ID URL Title Content rownum
52 b x x 2
41 d x x 4
thank you.
p.s. I don't quite understand the sql query actually even though I almost got the answer, for instance, what do the 'u' and 't' mean?
what do the 'u' and 't' mean?
They are table aliases, so you don't have to specify the entire name of the table when you need to make reference.
To get only the odd numbered records, use:
SELECT x.*
FROM (SELECT u.pg_id AS ID,
u.pg_url AS URL,
u.pg_title AS Title,
u.pg_content_1 AS Content,
#rownum := #rownum + 1 AS rownum
FROM root_pages u
JOIN (SELECT #rownum := 0) r
WHERE u.parent_id = '7'
AND u.pg_id != '7'
AND u.pg_cat_id = '2'
AND u.pg_hide != '1'
ORDER BY u.pg_created DESC) x
WHERE x.rownum % 2 != 0
To get the even numbered records, use:
SELECT x.*
FROM (SELECT u.pg_id AS ID,
u.pg_url AS URL,
u.pg_title AS Title,
u.pg_content_1 AS Content,
#rownum := #rownum + 1 AS rownum
FROM root_pages u
JOIN (SELECT #rownum := 0) r
WHERE u.parent_id = '7'
AND u.pg_id != '7'
AND u.pg_cat_id = '2'
AND u.pg_hide != '1'
ORDER BY u.pg_created DESC) x
WHERE x.rownum % 2 = 0
Explanation
The % is the modulus operator in MySQL syntax -- it returns the remainder of the division. For example 1 % 2 is 0.5, while 2 % 2 is zero. This is then used in the WHERE clause to filter the rows displayed.