I have the following select with the returned result of one row:
SELECT *
FROM (SELECT (SELECT dokdatum
FROM dokumente
WHERE dokid = '00100002LNWCAJ') AS tckdatum,
aktzeitdatum aktdatum,
(SELECT tckdokzeit
FROM tickets
WHERE tckid = '00100000000ICQ') tckzeit,
(hh * 60 + mm) * 60 AS aktzeit, firmaid
FROM (SELECT TO_DATE (aktdatumuhrzeitunitup,
'DD.MM.YYYY'
) aktzeitdatum,
a.firmaid,
TO_NUMBER (TO_CHAR (aktdatumuhrzeitunitup, 'HH24')
) hh,
TO_NUMBER (TO_CHAR (aktdatumuhrzeitunitup, 'MI')) mm
FROM aktivitaeten a, aktivitaetenarten aa
WHERE a.aktartid = aa.aktartid(+)
/* downtime terminated */
AND aktunitup = 1
AND tckid = '00100000000ICQ'
AND (aktartstatistik IS NULL OR aktartstatistik = 1)
AND a.aktdatumuhrzeitunitup IS NOT NULL)
ORDER BY ((aktdatum - tckdatum) * 24 * 60 * 60 + (aktzeit - tckzeit)
) DESC)
WHERE ROWNUM < 2;
Result is:
11.03.2016 || 11.03.2016 || 41334 || 41940 || 001
This statement is executable without errors.
But when I try to use it in a package function with
FUNCTION xy ...
IS
CURSOR c_cdt_aktdatumuhrzeitunitup
IS
--the above select
r_cdt_aktdatumuhrzeitunitup c_cdt_aktdatumuhrzeitunitup%ROWTYPE;
BEGIN
OPEN c_cdt_aktdatumuhrzeitunitup;
--Exception is thrown with this statement
FETCH c_cdt_aktdatumuhrzeitunitup
INTO r_cdt_aktdatumuhrzeitunitup;
Then I receive the ORA-01861: literal does not match format string error on the FETCH...INTO line
Does anyone know why?
Related
How could I get the date of the Maximum Value, by means of a subquery
I can't put the Date in the Main query because I would have to add it to the group by it would bring me a lot of data
Here is the Code:
SELECT MAX (A1.VALOR) AS VALOR,
(SELECT sq1.FECHA
FROM VARIABLE_VALORES_SMEC sq1
WHERE sq1.ID_AGENTE = A1.ID_AGENTE)
MES, -- {<-- Here is the Problem}
(SELECT CODIGO_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Agentess,
(SELECT NOMBRE_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Nombre_Agente
FROM VARIABLE_VALORES_SMEC A1
WHERE A1.VALOR < '1'
AND A1.VALOR != '0'
AND A1.ID_AGENTE IN (SELECT C1.ID_AGENTE
FROM VARIABLE_VALORES_SMEC C1
WHERE A1.FECHA = C1.FECHA)
AND A1.ID_AGENTE IN (SELECT B1.ID_AGENTE
FROM AGENTES B1
WHERE ID_CATEGORIA_AGENTE = 'AC006')
AND (A1.FECHA BETWEEN (ADD_MONTHS (TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))
AND (LAST_DAY (
ADD_MONTHS (
TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))))
AND A1.ID_VARIABLE LIKE '%_calc_total_pot#%'
GROUP BY ID_AGENTE
Am I correct that you need (fecha) for maximum A1.VALOR?
If - yes, you can use the following query, or if - no, just replace A1.VALOR with the required column in keep() clause:
SELECT MAX (A1.VALOR) AS VALOR,
max(A1.FECHA)keep(dense_rank first order by A1.VALOR desc) MES, -- A1.VALOR is used here as sort key, replace it with what you want
(SELECT CODIGO_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Agentess,
(SELECT NOMBRE_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Nombre_Agente
FROM VARIABLE_VALORES_SMEC A1
WHERE A1.VALOR < '1'
AND A1.VALOR != '0'
AND A1.ID_AGENTE IN (SELECT C1.ID_AGENTE
FROM VARIABLE_VALORES_SMEC C1
WHERE A1.FECHA = C1.FECHA)
AND A1.ID_AGENTE IN (SELECT B1.ID_AGENTE
FROM AGENTES B1
WHERE ID_CATEGORIA_AGENTE = 'AC006')
AND (A1.FECHA BETWEEN (ADD_MONTHS (TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))
AND (LAST_DAY (
ADD_MONTHS (
TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))))
AND A1.ID_VARIABLE LIKE '%_calc_total_pot#%'
GROUP BY ID_AGENTE
You can use row_number analytical function to fetch one record for which value is highest and use the fecha of that record. Use following sub query:
(Select fecha from
(SELECT sq1.FECHA, row_number() over (order by sq1.value desc nulls last) as rn
FROM VARIABLE_VALORES_SMEC sq1
WHERE sq1.ID_AGENTE = A1.ID_AGENTE)
Where rn = 1) MES
I'm using Oracle 12C and I have the following code:
SELECT *
FROM
(
SELECT
*
FROM
t.tablea
WHERE
name = 'FIS'
) A
LEFT JOIN (
SELECT
*
FROM
t.tableb
WHERE
enabled = 1
) B ON b.id = a.id
AND TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi')
Both a and b createdate are timestamp datatype.
Optimizer return an internal_function at TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi') in Execution Plan
If I compare like this: 'AND b.createdate = a.createdate', It will lost 1000 rows that look like this '11-JUN-18 04.48.34.269928000 PM'. And If I change 269928000 to 269000000 It will work
Now, I don't want to using to_char to avoid internal_function(must create Function-based-Index)
Anyone can help me?
If I compare like this: AND b.createdate = a.createdate, It will lost 1000 rows that look like this 11-JUN-18 04.48.34.269928000 PM. And If I change 269928000 to 269000000 It will work
Your values appear to have a fractional seconds component and would have the TIMESTAMP data type. If so, you can use TRUNC( timestamp_value, 'MI' ) to truncate to the nearest minute.
SELECT *
FROM t.tablea a
LEFT OUTER JOIN t.tableb b
ON ( a.createdate >= TRUNC( b.createdate, 'MI' )
AND a.createdate < TRUNC( b.createdate, 'MI' ) + INTERVAL '1' MINUTE
AND a.id = b.id
AND b.enabled = 1
)
WHERE a.name = 'FIS'
This will remove the need to apply a function to one of the two tables (a.createdate in this case but you could swap them).
I don't even see the need for the subqueries:
SELECT a.*, b.*
FROM t.tablea a
LEFT JOIN t.tableb b
ON a.id = b.id AND
TRUNC(b.createdate, 'MI') = TRUNC(a.createdate, 'MI') AND
b.enabled = 1
WHERE
a.name = 'FIS'
I have been running the below query without issue:
with Nums (NN) as
(
select 0 as NN
from dual
union all
select NN+1 -- (1)
from Nums
where NN < 30
)
select null as errormsg, trunc(sysdate)-NN as the_date, count(id) as the_count
from Nums
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = trunc(sysdate)-Nums.NN
group by trunc(sysdate)-Nums.NN
However, when I try to pop this in a proc for SSRS use:
procedure pr_do_the_thing (RefCur out sys_refcursor)
is
oops varchar2(100);
begin
open RefCur for
-- see above query --
;
end pr_do_the_thing;
I get
Error(): PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got -
Any thoughts? Like I said above, as a query, there is no issue. As a proc, the error appears at note (1) int eh query.
This seems to be bug 18139621 (see MOS Doc ID 2003626.1). There is a patch available, but if this is the only place you encounter this, it might be simpler to switch to a hierarchical query:
with Nums (NN) as
(
select level - 1
from dual
connect by level <= 31
)
...
You could also calculate the dates inside the CTE (which also fails with a recursive CTE):
with Dates (DD) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select null as errormsg, DD as the_date, count(id) as the_count
from Dates
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = DD
group by DD;
I'd probably organise it slightly differently, so the subquery doesn't limit the date range directly:
with dates (dd) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select errormsg, the_date, count(id) as the_count
from (
select null as errormsg, d.dd as the_date, c1.id
from dates d
left join table1 c1 on c1.c_date >= d.dd and c1.c_date < d.dd + 1
union all
select null as errormsg, d.dd as the_date, c2.id
from dates d
left join table2 c2 on c2.c_date >= d.dd and c2.c_date < d.dd + 1
)
group by errormsg, the_date;
but as always with these things, check the performance of each approach...
Also notice that I've switched from union to union all. If an ID could appear more than once on the same day, in the same table or across both tables, then the counts will be different - you need to decide whether you want to count them once or as many times as they appear. That applies to your original query too.
I am trying to run the following query. I am passing the date from a parameter
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-Apr-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
Error in query: ORA-01481: invalid number format model
The following query works. I am just trying to change sysdate to a date string which I pass as param through user input in php. Why it is being multiplied by 1 I do not know, it is a query that works and provides the required result.
SELECT to_char(TIMESTAMP, 'HH24') * 1
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(sysdate-1)
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
The problem is the last line:
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
In the order-by clause it's seeing TIMESTAMP as the column alias from the select list, not as the original TIMESTAMP from the table; so it contains a number, not a timestamp.
So if you had a timestamp where the hours value was 13, say, you're doing
ORDER BY to_char(13, 'HH24') * 1`
... and as the first argument is a number you're using the numeric version of TO_CHAR(), and HH24 isn't a valid format model for that. Which is what the error message says.
You can just do:
ORDER BY TIMESTAMP;
To use your original pattern you'd need to supply a table alias for your inline view, and then use that alias in the order-by, to qualify the TIMESTAMP. By default it's using the column alias version.
order by is executed at last. By the time order by is executed, you are deriving an new column in select statement as timestamp. To overcome it, just use timestamp in order by
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-APR-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY TIMESTAMP -- TIMESTAMP here refers to derived column at line 1
Or use this
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP1 -- Or use different alias
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-APR-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
I replicated the issue here. - This will not work.
select to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP,sum(col1) from
(
select sysdate-1 as TIMESTAMP,1 as col1 from dual
union all
select sysdate-1 as TIMESTAMP,2 as col1 from dual
)
where trunc(sysdate)=trunc(to_date('04-APR-16','Dd-Mon-YY'))
group by to_char(TIMESTAMP, 'HH24') * 1
order by to_char(TIMESTAMP, 'HH24') * 1
But This statement will work.
select to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP1,sum(col1) from (
select sysdate-1 as TIMESTAMP,1 as col1 from dual
union all
select sysdate-1 as TIMESTAMP,2 as col1 from dual
)
where trunc(sysdate)=trunc(to_date('04-APR-16','Dd-Mon-YY'))
group by to_char(TIMESTAMP, 'HH24') * 1
order by to_char(TIMESTAMP, 'HH24') * 1 --As TIMESTAMP is now reflecting the column from inner table.
I am using Oracle Database 11g Enterprise Edition Release 11.2.0.2.0
I have a table as follows:
Table1:
Name Null Type
----------------- -------- -------------
NAME NOT NULL VARCHAR2(64)
VERSION NOT NULL VARCHAR2(64)
Table1
Name Version
---------------
A 1
B 12.1.0.2
B 8.2.1.2
B 12.0.0
C 11.1.2
C 11.01.05
I want the output as:
Name Version
---------------
A 1
B 12.1.0.2
C 11.01.05
Basically, I want to get the row for each name which have highest version. For this I am using the following query:
SELECT t1.NAME,
t1.VERSION
FROM TABLE1 t1
LEFT OUTER JOIN TABLE1 t2
on (t1.NAME = t2.NAME and t1.VERSION < t2.VERSION)
where t2.NAME is null
Now 't1.VERSION < t2.VERSION' only works in normal version cases but in cases such as:
B 12.1.0.2
B 8.2.1.2
It fails, I need a PL/SQL script to normalize the version strings and compare them for higher value.
You can do this with judicious use of REGEXP_SUBSTR(); there's no need to use PL/SQL.
select *
from ( select a.*
, row_number() over (
partition by name
order by to_number(regexp_substr(version, '[^.]+', 1, 1)) desc
, to_number(regexp_substr(version, '[^.]+', 1, 2)) desc
, to_number(regexp_substr(version, '[^.]+', 1, 3)) desc
, to_number(regexp_substr(version, '[^.]+', 1, 4)) desc
) as rnum
from table1 a )
where rnum = 1
Here's a SQL Fiddle to demonstrate. Please note how I've had to convert each portion to a number to avoid a binary sort on numbers not between 0 and 9.
However, I cannot emphasise enough how much easier your life would be if you separated these up into different columns, major version, minor version etc. You could then have a virtual column that concatenates them all together to ensure that your export is always standardised, should you wish.
If, for instance, you created a table as follows:
create table table1 (
name varchar2(64)
, major number
, minor number
, build number
, revision number
, version varchar2(200) generated always as (
to_char(major) || '.' ||
to_char(minor) || '.' ||
to_char(build) || '.' ||
to_char(revision)
)
Your query becomes simpler to understand; also in SQL Fiddle
select name, version
from ( select a.*
, row_number() over (
partition by name
order by major desc
, minor desc
, build desc
, revision desc ) as rnum
from table1 a )
where rnum = 1
This solution is independent on how many numerical parts are inside version code.
It only assumes that every numerical part consists of not more than 6 digits.
select
name,
max(version) keep (dense_rank first order by version_norm desc)
as max_version
from (
select
t.*,
regexp_replace(
regexp_replace('000000'||version, '\.', '.000000')||'.',
'\d*(\d{6}\.)', '\1')
as version_norm
from table1 t
)
group by name
SQL Fiddle
You somehow need to convert the string values into numeric values, and then scale them by some appropriate multiplier. Assume that each version value must be a number between 0..99 as an example. So, if your string was "8.2.1.2", you would scale the numeric values of the string, "a.b.c.d" = d + c*100 + b*10000 + a*1000000, = 2 + 100 + 20000 + 8000000 =
8020102, then you can use that value to order.
I found a function you can use to parse a token from a delimited string:
CREATE OR REPLACE FUNCTION get_token (the_list VARCHAR2,
the_index NUMBER,
delim VARCHAR2 := ',')
RETURN VARCHAR2
IS
start_pos NUMBER;
end_pos NUMBER;
BEGIN
IF the_index = 1
THEN
start_pos := 1;
ELSE
start_pos :=
INSTR (the_list,
delim,
1,
the_index - 1);
IF start_pos = 0
THEN
RETURN NULL;
ELSE
start_pos := start_pos + LENGTH (delim);
END IF;
END IF;
end_pos :=
INSTR (the_list,
delim,
start_pos,
1);
IF end_pos = 0
THEN
RETURN SUBSTR (the_list, start_pos);
ELSE
RETURN SUBSTR (the_list, start_pos, end_pos - start_pos);
END IF;
END get_token;
so call something like
select to_number(get_token(version,1,'.'))*1000000 + to_number(get_token(version,2,'.'))*10000 + .. etc.
Just wrote a MySQL user defined function to accomplish the task, you can easily port it to ORACLE PL/SQL.
DELIMITER $$
DROP FUNCTION IF EXISTS `VerCmp`$$
CREATE FUNCTION VerCmp (VerX VARCHAR(64), VerY VARCHAR(64), Delim CHAR(1))
RETURNS INT DETERMINISTIC
BEGIN
DECLARE idx INT UNSIGNED DEFAULT 1;
DECLARE xVer INT DEFAULT 0;
DECLARE yVer INT DEFAULT 0;
DECLARE xCount INT UNSIGNED DEFAULT 0;
DECLARE yCount INT UNSIGNED DEFAULT 0;
DECLARE counter INT UNSIGNED DEFAULT 0;
SET xCount = LENGTH(VerX) - LENGTH(REPLACE(VerX, Delim,'')) +1;
SET yCount = LENGTH(VerY) - LENGTH(REPLACE(VerY, Delim,'')) +1;
IF xCount > yCount THEN
SET counter = xCount;
ELSE
SET counter = yCount;
END IF;
WHILE (idx <= counter) DO
IF (xCount >= idx) THEN
SET xVer = SUBSTRING_INDEX(SUBSTRING_INDEX(VerX, Delim, idx), Delim, -1) +0;
ELSE
SET xVer =0;
END IF;
IF (yCount >= idx) THEN
SET yVer = SUBSTRING_INDEX(SUBSTRING_INDEX(VerY, Delim, idx), Delim, -1) +0;
ELSE
SET yVer = 0;
END IF;
IF (xVer > yVer) THEN
RETURN 1;
ELSEIF (xVer < yVer) THEN
RETURN -1;
END IF;
SET idx = idx +1;
END WHILE;
RETURN 0;
END$$;
DELIMITER ;
Few test that I ran:
select vercmp('5.2.4','5.2.5','.');
+------------------------------+
| vercmp('5.2.4','5.2.5','.') |
+------------------------------+
| -1 |
+------------------------------+
select vercmp('5.2.4','5.2.4','.');
+------------------------------+
| vercmp('5.2.4','5.2.4','.') |
+------------------------------+
| 0 |
+------------------------------+
select vercmp('5.2.4','5.2','.');
+----------------------------+
| vercmp('5.2.4','5.2','.') |
+----------------------------+
| 1 |
+----------------------------+
select vercmp('1,2,4','5,2',',');
+----------------------------+
| vercmp('1,2,4','5,2',',') |
+----------------------------+
| -1 |
+----------------------------+