Selecting rownum/max(rownum) with jOOQ - java

I'm looking at a SQL query that uses Oracle's rownum pseudocolumn to select the row number as a fraction of the total rows:
ROWNUM/(MAX(ROWNUM) OVER())
I'm trying to accomplish the same thing with a query written via jOOQ. Is that possible?

I'm not sure I got your question right - is it that you need ?
SQL> select row_number() over(order by ename)/(count(*) over()) fraction from emp;
FRACTION
----------
.08333
.16667
.25000
.33333
.41667
.50000
.58333
.66667
.75000
.83333
.91667
1.00000
Also CUME_DIST analytic function can be useful:
SQL> SELECT ename, CUME_DIST()
2 OVER (ORDER BY ename) fraction
3 FROM emp
4 /
ENAME FRACTION
---------- ----------
ALLEN .08333
BLAKE .16667
CLARK .25000
FORD .33333
JAMES .41667
JONES .50000
KING .58333
MARTIN .66667
MILLER .75000
SMITH .83333
TURNER .91667
WARD 1.00000

While Dmitry's CUME_DIST() solution is probably better suited for the actual query, here's the ROWNUM solution in jOOQ, for the record:
// Qualified
DSL.rownum().div(DSL.max(DSL.rownum()).over());
// With static imports of DSL.*
rownum().div(max(rownum()).over());
See also: DSL.rownum()

Related

Oracle order by via JPA with numeric first

I have column data like this in my db
data
-----
1
2
A
3
4
B
I have below nls settings in place
SELECT * From NLS_SESSION_PARAMETERS;
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_COMP BINARY
When I query the db for **select data from <mytable> order by data ** I am getting the result as below
data
-----
1
2
3
4
A
B
The same query via hibernate is giving the results with alphabets first and numeric later
data
-----
A
B
1
2
3
4
But, I want column to be displayed with numbers first and alphabets later via hibernate
data
------
1
2
3
4
A
B
Can someone help me with this.
You can use regularexpressions to sort in such a way,
select data from <mytable> order by REGEXP_REPLACE(data ,'[^0-9]'), REGEXP_REPLACE(data ,'[0-9]')
That depends on NLS_SORT parameter's value. For example, in my database it is set to CROATIAN and the result is what you wanted:
SQL> select * From nls_session_parameters where parameter = 'NLS_SORT';
PARAMETER VALUE
-------------------- --------------------
NLS_SORT CROATIAN
SQL> with test (data) as
2 (select '1' from dual union all
3 select '2' from dual union all
4 select 'A' from dual union all
5 select '3' from dual union all
6 select '4' from dual union all
7 select 'B' from dual
8 )
9 select data
10 from test
11 order by data;
D
-
A
B
1
2
3
4
6 rows selected.
However, in your database, NLS_SORT = BINARY. Let's try it:
SQL> alter session set nls_sort = 'BINARY';
Session altered.
SQL> with test (data) as
2 (select '1' from dual union all
3 select '2' from dual union all
4 select 'A' from dual union all
5 select '3' from dual union all
6 select '4' from dual union all
7 select 'B' from dual
8 )
9 select data
10 from test
11 order by data;
D
-
1
2
3
4
A
B
6 rows selected.
SQL>
Right; the wrong result.
Therefore, modify NLS_SORT, if that's an option. See valid values by
SQL> select * From v$nls_valid_values where parameter = 'SORT';
PARAMETER VALUE ISDEP
-------------------- -------------------- -----
SORT BINARY FALSE
SORT WEST_EUROPEAN FALSE
SORT XWEST_EUROPEAN FALSE
SORT GERMAN FALSE
<snip>
I believe one of the two options below should work,
Use NLSSORT function in the order by clause explicitly.
select * from my_table order by NLSSORT(data,'NLS_SORT=BINARY');
Create a function based index on the column,
create index mytable_nlssort_index on my_table(nlssort(data, 'nls_sort=''BINARY'''))
Use the query as below when the column is indexed.
select data from my_table order by data;

How to update two tables together in Oracle?

Query :
UPDATE TABLE_ONE
SET DATE=?, URL=?, TYPE=?, STATE=?, FEE=?, NAME=?, STATUS=?
WHERE ID=?";
Another table TABLE_TWO has columns - NAME, ID, FEE, STATUS, TOTAL,I want that on running update all above fields specified in query plus FEE, STATUS of TABLE_TWO gets updated together. I'm using Spring.
You can use trigger for say Table1 & write a trigger if any update on table 1 call trigger to update the Table 2 data.
it starts like..
create or replace TRIGGER trigger_name
after insert or update on Table1
for each row
...
For complete trigger refer (https://www.tutorialspoint.com/plsql/plsql_triggers.htm)
You can't update 2 tables (more than one) in one statement, instead use 2 statements and call them inside method annotated with #Transactional
#Transactional
public void updateTables() {
updateTableOne();
updateTableTwo();
}
commit will happen when method exists for both tables.
See more about using #Transactional in Spring
Other option is calling oracle procedure
Spring provides various ways of abstractions on JDBC to call database stored procedures.
Example
SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("MOVE_TO_HISTORY")
Solution 1: You can add both update statements in PLSQL procedure/function and execute the same in java code
Solution 2: You can add both update statements in a function and invoke the same
Oracle way of doing that is an instead-of trigger on a view.
Here's an example based on Scott's schema.
First, I'll create a view as a join of two tables, EMP and DEPT (that's where updating two tables together comes into a game):
SQL> create or replace view v_ed as
2 select d.deptno, e.empno, d.dname, e.ename, e.sal
3 from emp e join dept d on e.deptno = d.deptno;
View created.
Now, an instead-of trigger. I'm handling INSERT and UPDATE; you can add DELETE as well. It means that - when you insert into a view or update it, underlying tables will be the final target of those commands.
SQL> create or replace trigger trg_io_ed
2 instead of insert or update on v_ed
3 for each row
4 begin
5 if inserting then
6 insert into emp (deptno, empno, ename, sal)
7 values (:new.deptno, :new.empno, :new.ename, :new.sal);
8 insert into dept (deptno, dname)
9 values (:new.deptno, :new.dname);
10 elsif updating then
11 update emp set
12 deptno = :new.deptno,
13 ename = :new.ename,
14 sal = :new.sal
15 where empno = :new.empno;
16 update dept set
17 dname = :new.dname
18 where deptno = :new.deptno;
19 end if;
20 end;
21 /
Trigger created.
Some testing: insert:
SQL> insert into v_ed (deptno, empno, dname, ename, sal)
2 values (99, 100, 'test dept', 'Littlefoot', 1000);
1 row created.
SQL> select * From dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
99 test dept
SQL> select * From emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.80 800 20
7499 ALLEN SALESMAN 7698 20.02.81 1600 300 30
7521 WARD 7698 22.02.81 1250 500
7566 JONES MANAGER 7839 02.04.81 2975 20
7654 MARTIN SALESMAN 7698 28.09.81 1250 1400 30
7698 BLAKE MANAGER 7839 01.05.81 2850
7782 CLARK MANAGER 7839 09.06.81 2450 10
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7839 KING PRESIDENT 17.11.81 5000 10
7844 TURNER SALESMAN 7698 08.09.81 1500 0 30
7876 ADAMS CLERK 7788 12.01.83 1100 20
7900 JAMES CLERK 7698 03.12.81 950 30
7902 FORD ANALYST 7566 03.12.81 3000 20
7934 MILLER CLERK 7782 23.01.82 1300 10
100 Littlefoot 1000 99
15 rows selected.
SQL>
Update:
SQL> update v_ed set ename = 'Bigfoot' where empno = 100;
1 row updated.
SQL> select * From emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.80 800 20
7499 ALLEN SALESMAN 7698 20.02.81 1600 300 30
7521 WARD 7698 22.02.81 1250 500
7566 JONES MANAGER 7839 02.04.81 2975 20
7654 MARTIN SALESMAN 7698 28.09.81 1250 1400 30
7698 BLAKE MANAGER 7839 01.05.81 2850
7782 CLARK MANAGER 7839 09.06.81 2450 10
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7839 KING PRESIDENT 17.11.81 5000 10
7844 TURNER SALESMAN 7698 08.09.81 1500 0 30
7876 ADAMS CLERK 7788 12.01.83 1100 20
7900 JAMES CLERK 7698 03.12.81 950 30
7902 FORD ANALYST 7566 03.12.81 3000 20
7934 MILLER CLERK 7782 23.01.82 1300 10
100 Bigfoot 1000 99
15 rows selected.
SQL>
See if it helps.

Symptoms and its disease database design

Im working with android health application which will display human body parts such as head, chest etc.. For now I have three tables such as symptoms, diseases and symptom_disease table which will link those two table. If user select symptom such as dizziness, syncope, asthenia and others, then it will display hypertensive disease. But it will have problem when user select only one symptoms let say dizziness, it will also display the same disease. How do i differentiate this things? and how to implement it.
This is my symptom table
s_id | s_name | s_part
1 |dizziness | Head
2 |syncope | Head
3 |asthenia | Head
Disease table
d_id | d_name | d_desc
1 |hypertensive disease| ....
Symptom_disease table
s_id | d_id
1 | 1
2 | 1
3 | 1
I use this query to get the disease
SELECT d.d_name, d.d_desc, s.symp_name
FROM symtoms s
LEFT JOIN symptom_disease sd ON sd.sid = s.sid
LEFT JOIN diseases d ON d.did = sd.did
WHERE s.sid IN (1,2,3) GROUP BY d.d_name
The problem here is when user select only one disease, I dont want it show the disease because one symptoms does not show that the user is ill. So, I need a suggestion on how to implement this. I have see a solution which require me to add rank/weight inside symptom_disease table. But I dont know how to implement this method. Thanks in advance.
Here's a basic approach to use something like a probability column to determine the likelihood of a particular disease.
For diseases table:
1 Flu
2 Lyme
For symptoms table:
1 Muscle Aches
2 Headache
3 Fatigue
4 Fever
5 Vomiting
6 Tick Bite
For disease_symptom table:
s_id d_id probability
1 1 0.2
2 1 0.2
3 1 0.2
4 1 0.2
5 1 0.2
1 2 0.1
2 2 0.1
3 2 0.1
4 2 0.1
6 2 0.6
So if a patient says that have symptoms 2, 4, and 5, then
select d_name, sum(probability)
from disease, disease_symptom
where disease.d_id = disease_symptom.d_id
and s_id in (2, 4, 5)
group by d_name
order by sum(probability) desc
is
Flu 0.6
Lyme 0.2
so Flu is the most likely.
But if the patient has symptoms 2, 4, and 6, then
select d_name, sum(probability)
from disease, disease_symptom
where disease.d_id = disease_symptom.d_id
and s_id in (2, 4, 6)
group by d_name
order by sum(probability) desc
is
Lyme 0.8
Flue 0.4
so Lyme disease is mostly likely.
This may not be the most elegant way of doing it, but it should work, assuming you're using MySql:
select *
from
(
select d.d_name, d.d_desc, GROUP_CONCAT(sd.s_id SEPARATOR ',') s_ids
from Diseases d , Symptom_disease sd
where sd.d_id = d.d_id
group by d.d_name, d.d_desc
order by sd.s_id
) as t
where t.s_ids = '1,2,3'
;
You need to make sure you concatenate all the sympton ids in ascending order in the where clause of the outer select ( where t.s_ids = '1,2,3'), and it should give you only the diseases whose symptons match exactly the ones your users selected.

H2 DB - Column must be in Group By list

i am using H2-DB to access static databases...
i have a table which looks like:
COUNTRY STATE CITY LAT LNG COUNTRYID STATEID CITYID
"Germany" "Berlin" "" 1.23 1.23 1 1 0
"Germany" "München" "" 1.23 1.23 1 2 0
"USA" "Alabama" "Auburn" 1.23 1.23 2 1 1
"USA" "Alabama" "Birmingham" 1.23 1.23 2 1 2
"USA" "Alaska" "Anchorage" 1.23 1.23 2 2 1
"USA" "Alaska" "Cordova" 1.23 1.23 2 2 2
its a huge list with lots of countries, most of them just have Country and State (like Germany here, whereas State's are Cities), a few also have a City (like USA here)...
the problem is now, when i query
SELECT * FROM MyTable WHERE COUNTRY = 'Germany' group by STATE order by STATE
to get a sorted list of the states (or cities), i get an error saying
Field CITY must be in the GROUP BY list
if the row has a city, i need the whole row, otherwise i would only need the State column, but i can just know after having queried it, wether it uses the city column or not, so i have to query "*" instead of "STATE"
the query should be okay, or? On MySql it is properly working... so whats the problem here?
Found this if it helps: http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90016
Metin
MySQL is broken in regards to this. It allows columns in the GROUP BY that are neither in the group by nor arguments to aggregation functions. In fact, the documentation warns against using this extension.
So you can do:
SELECT state
FROM DIYANET
WHERE COUNTRY = 'Germany'
GROUP BY STATE
ORDER BY STATE;
Or something like this:
SELECT state, min(city), min(lat), . . .
FROM DIYANET
WHERE COUNTRY = 'Germany'
GROUP BY STATE
ORDER BY STATE;
But SELECT * is not allowed and doesn't really make sense.

Hibernate criteria to fetch the records?

I have below records in table.
col1 col2 col3
------------------------
1 Abc IN
2 DEF CA
3 Xyz IN
4 Cae CA
5 Pty IN
6 Zwe DE
7 Zwf US
Here User sends an Input like IN or CA or DE etc. User input has to be mapped against col3. Now I need to query all the records from the table but the records matching the user input (IN or CA or DE) should appear first in the list then all other records should appear. How can I do it using hibernate criteria?
I need the results in below the order if user sends IN as an input.
1 Abc IN
5 Pty IN
3 Xyz IN
2 DEF CA
4 Cae CA
6 Zwe DE
7 Zwf US
You could try to use ORDER BY CASE construct:
order by case when <your entity>.col3 = :parameter then '0' else '1' end asc
There are two ways to solve this problem:
1. Create two queries one with equal to another with not equal to and all results for both in single list.
2. If you don't want to query database twice then you have to write algo in java that will remove elements for your input from list and add it another list and after iteration add remaining list at the end.
try the case ... when statements:
select *, ( case when col3=:input then '0' | col3 else col3 end) as sorter from table order by sorter asc
not sure, if it works, but if it does it would be exactly what you want

Categories