i have a following PREFERENCE_SCORE table
ID(int) | SCORE(Double) | RANKING(int) |
I want to insert into database using the following code
String sql = "INSERT INTO PREFERENCE_SCORE_ ( ID,
SCORE,RANK) VALUES (?,?,?) ";
String query ="SELECT ID,FPIS,FNIS FROM db_housekeep";
ResultSet rsp = stats.executeQuery(query);
while(rsp.next()){
int id = rsp.getInt(1);
double fpis = rsp.getDouble(2);
double fnis = rsp.getDouble(3);
prSt.setInt(1, id);
prSt.setDouble(2, fnis/(fnis+fpis));
prSt.setInt(3, THIS PART i dont understand)
prSt.executeUpdate();
}
What should i add so the RANKING column is automatically filled by the rank of the data according to the SCORE column?
I think this approach is not beneficial. It will generate ambiguous data for rank.
e.g. if we insert the first row with (score 99) the rank will be 1 and after it, we insert the second row with score 100 what is the rank? (according to runtime calculation rant should 1 but we have already 1 so what can we do?).
So, its prefer to use it only will select data from the table for showing.
Hopefully, you know the syntax as ( RANK ( ) OVER ( [ partition_by_clause ] order_by_clause ) )
Related
Below is a sample of what I did
{
...
Class.forName("com.mysql.jdbc.Driver").newInstance();
con=DriverManager.getConnection(ConnectionStr,"root","root");
String prepareStr="DELETE FROM customer_maintenance where id=?";
PreparedStatement pst= con.prepareStatement(prepareStr);
pst.setInt(1,id);
pst.executeUpdate();
String update_key="SET #count = 0; UPDATE customer_maintenance SET customer_maintenance.id = #count:= #count + 1;";
PreparedStatement pst1=con.prepareStatement(update_key);
pst1.executeUpdate();
System.out.println("UPDATE");
}
It is throwing the following exception
com.mysql.jdbc.exceptions.MySQLSyntaxErrorException:
You have an error in your SQL syntax
and I am not sure if it is possible to update the primary key, every time a row is deleted.
You should provide the exception you get, but the first problem I see in your code is that you may try to update a row setting ID = x, while another row could still have ID = x, thus an error for duplicate key.
That's because that update statement won't guarantee that the rows are updated following the ID order.
With the values in your example:
Start Delete Update1 Update2
ID ID ID ID
1 (del)
2 2 2 2
3 3 1 (upd) 1
4 4 4 2 (upd) <- duplicate key
Edit
Easiest fix is adding an order by clause to your query, so that the rows are update starting from the smaller ID.
You should also get rid of the ; at the end of the query.
String update_key="SET #count = 0;
UPDATE customer_maintenance
SET customer_maintenance.id = #count:= #count + 1
ORDER BY customer_maintenance.id";
If you have set auto increment and primary key for Id column while table creation. Auto increment will handle this automatically while database deletion, no need to worry about it.
there's column I want to retrieve and insert into another table
For example, below is first table I want to retrieve values
Table1
Records
1 ABC Singapore
2 DEF Vietnam
I retrieve above column value from Table1, then insert into another table as below
Table 2
ID Name Country
1 ABC Singapore
2 DEF Vietname
Currently, I can do with java, I first retrieve records then split the values and insert. However, I want to do it by batch or pagination for better performance when Table1 got million of records to retrieve and insert those million records into Table2.
Any pointer to show me how to use pagination in my case would be appreciated.
I"m use MSSQL 2008
If you need to do that in code (and not in SQL which should be easier even with multiple delimiters), what you probably want to use would be batched inserts with proper batch size combined with a good fetch-size on your select:
//Prepare statements first
try(PreparedStatement select = con.prepareStatement("SELECT * FROM SOURCE_TABLE");
PreparedStatement insert = con.prepareStatement("INSERT INTO TARGET_TABLE(col1, col2, col3) VALUES (?,?,?)")) {
//Define Parameters for SELECT
select.setFetchDirection(ResultSet.FETCH_FORWARD);
select.setFetchSize(10000);
int rowCnt = 0;
try(ResultSet rs = select.executeQuery()) {
while(rs.next()) {
String row = rs.getString(1);
String[] split = row.split(" |\\$|\\*"); //However you want to do that
//Todo: Error handling for array length
//Todo: Type-Conversions, if target data is not a string type
insert.setString(1, split[0]);
insert.setString(2, split[1]);
insert.setString(3, split[2]);
insert.addBatch();
//Submit insert in batches of a good size:
if(++rowCnt % 10000 == 0) {
int[] success = insert.executeBatch();
//Todo: Check if that worked.
}
}
//Handle remaining inserts
int[] success = insert.executeBatch();
//Todo: Check if that worked.
}
} catch(SQLException e) {
//Handle your Exceptions
}
On calculating on "good" fetch and batch sizes you'll want to consider some parameters:
Fetchsize impacts memory consumption in your client. If you have enough of that you can make it big.
Committing an insert of millions of rows will take some time. Depending on your requirements you might want to commit the insert transaction every once in a while (every 250.000 inserts?)
Think about your transaction isolation: Make sure auto-commit is turned off as committing each insert will make most of the batching gains go away.
I'm trying to implement some code for get friends list.
First:
- I have my String id. E.G: 784717
- I have an string with 100 numbers. E.G: 7781,5913,551949194,4919491,...,444131 (One string separated by ,)
- I have 3000 records in my Database with different numbers. (With numbers I mean some kind of ID)
- Of my 100 numbers only 8 are registered in my database.
Question:
How can I know what numbers are registered in the database and insert in other table the relationship?
My table Relationship have this columns:
*number1 - (Here should be my ID)
*number2 - (1 of the 100 numbers that exists)
So in my table Relationship should be 8 new rows.
I tried with :
EXEC('SELECT * FROM Accounts WHERE ID IN(' +#in_mystring+')')
but i don't know how insert in the other table or if is efficiently
Assuming this is SQL Server, and with the help of a parser function
For example
Select * from [dbo].[udf-Str-Parse]('7781,5913,551949194,4919491,...,444131',',')
Returns
Key_PS Key_Value
1 7781
2 5913
3 551949194
4 4919491
5 ...
6 444131
From this sub-query, you can join the results to your Accounts Table
Perhaps something like this
Select A.*
From Accounts A
Join (Select * from [dbo].[udf-Str-Parse]('7781,5913,551949194,4919491,...,444131',',')) B
on A.Key_Value =A.ID
The UDF -- If 2016, There is a native parser.
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
-- Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
-- Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')
Returns #ReturnTable Table (Key_PS int IDENTITY(1,1) NOT NULL , Key_Value varchar(max))
As
Begin
Declare #intPos int,#SubStr varchar(max)
Set #IntPos = CharIndex(#delimeter, #String)
Set #String = Replace(#String,#delimeter+#delimeter,#delimeter)
While #IntPos > 0
Begin
Set #SubStr = Substring(#String, 0, #IntPos)
Insert into #ReturnTable (Key_Value) values (#SubStr)
Set #String = Replace(#String, #SubStr + #delimeter, '')
Set #IntPos = CharIndex(#delimeter, #String)
End
Insert into #ReturnTable (Key_Value) values (#String)
Return
End
CREATE or REPLACE PROCEDURE TEST(
activationStartDate IN DATE,
activationEndDate IN DATE,
deActivationStartDate IN DATE,
deActivationEndDate IN DATE
)
AS
FirstNameListTable LIST_TABLE;
{--COMMENT :LIST_TABLE is nested table :create or replace TYPE "LIST_TABLE" as table of varchar2(20);-----Nested Table Declaration
/
}
totalSameFirstName NUMBER;
j NUMBER := 1;
BEGIN
SELECT first_name BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate
MINUS
SELECT first_name FROM Employee where start_date between deActivationStartDate AND deActivationEndDate
FOR i IN FirstNameListTable.FIRST .. FirstNameListTable.LAST LOOP
SELECT count(*) INTO totalSameFirstName FROM Employee where start_date between activationStartDate AND activationEndDate AND first_name=FirstNameListTable(i)
IF totalSameFirstName > 2 THEN
---business logic
END IF;
END LOOP;
Actually there are two queries as mensioned above
Letsay this query as Q1:
SELECT first_name BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate
MINUS
SELECT first_name FROM Employee where start_date between deActivationStartDate AND deActivationEndDate
and this query as Q2 :
SELECT count(*) INTO totalSameFirstName FROM Employee where start_date between activationStartDate AND activationEndDate AND first_name=FirstNameListTable(i)
In both the qyery i am scanning complete table,which i think there is no need. I am iterating result of Q1 and then again scanning the table to count similar first_name. If a particular firstName occur more than two times i wrote business logic.
Can i combine both the queries,Means i want to store result of Q1 in some PL/SQL dataStructure and want to perform Q2 on result of Q1.
I want to modify Q1 as
SELECT * BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate
MINUS
SELECT * FROM Employee where start_date between deActivationStartDate AND deActivationEndDate
But how to store 'select *' result in pl/sql dataStructure and How to pass these records to second query..Can u tell me how my code will look like?
try to be more clear, i'm in lack of ideas in this PL/SQL, even it sounds like a classic :I have spend hours trying to play around with this but have got nowhere
#Ollie
I changed the code as suggested by u but getting some error and not able to solve them
CREATE or REPLACE PROCEDURE TEST(
activationStartDate IN DATE,
activationEndDate IN DATE,
deActivationStartDate IN DATE,
deActivationEndDate IN DATE,
Out_Entity OUT TEST1.RefCsr
)
AS
FirstNameListTable CRITERIA_LIST_TABLE;
out NUMBER;
j NUMBER := 1;
CURSOR main_cur
IS
WITH include_rec
AS (SELECT first_name,COUNT(1) OVER (PARTITION BY first_name) name_count FROM employee where start_date between activationStartDate AND activationEndDate
MINUS
SELECT first_name FROM employee where start_date between deActivationStartDate AND deActivationEndDate)
SELECT first_name FROM include_rec WHERE name_count > 2;
BEGIN
OPEN main_cur;
FETCH main_cur BULK COLLECT INTO FirstNameListTable;
CLOSE main_cur;
OPEN Out_Entity FOR SELECT * FROM TABLE(
CAST (
FirstNameListTable AS LIST_TABLE
)
) Nos;
END;
/
Error 1: PL/SQL: SQL Statement ignored {Indicating : "WITH include_rec" Line}
Error 2 : PL/SQL: ORA-01789: query block has incorrect number of result columns {Indicating line : "AS (SELECT first_name,COUNT(1) OVER (PARTITION BY first_name)"}
thanks in advance
waiting for ur reply
I'd like to caveat this by saying that I haven't had a lot of time to work on this so there may be a few errors but it should give you the gist of what i'm trying to tell you:
CREATE or REPLACE
PROCEDURE TEST(
activationStartDate IN DATE,
activationEndDate IN DATE,
deActivationStartDate IN DATE,
deActivationEndDate IN DATE )
AS
CURSOR main_cur
IS
WITH include_rec
AS (SELECT first_name,
start_date,
COUNT(1) OVER (PARTITION BY first_name) name_count
FROM Employee
WHERE start_date BETWEEN activationStartDate
AND activationEndDate)
SELECT DISTINCT
first_name
FROM include_rec
WHERE start_date NOT BETWEEN deActivationStartDate
AND deActivationEndDate
AND name_count > 2;
--
FirstNameListTable dbms_sql.varchar2_table;
BEGIN
OPEN main_cur;
FETCH main_cur BULK COLLECT INTO FirstNameListTable;
CLOSE main_cur;
FOR i IN FirstNameListTable.FIRST .. FirstNameListTable.LAST
LOOP
---business logic
END LOOP;
etc...
I'd also say that if you are expecting a large resultset then put the BULK COLLECT into a loop too to reduce your memory requirements.
In an ideal world, you'd pass the variables activationStartDate, activationEndDate, deActivationStartDate and deActivationEndDate into the cursor as cursor parameters to keep the cursor modular but that's up to you. ;-)
Hope this helps...
Ollie.
EDIT:
In response to your question about using records to select * from the table, you can declare an associative array based on the cursor columns, for the example above if you wanted to select more than one column from EMPLOYEE then after the WITH clause you would select the named columns from EMPLOYEE you want and instead of:
FirstNameListTable dbms_sql.varchar2_table;
declare the associative array type and variable as:
TYPE main_cur_tabtype IS TABLE OF main_cur%ROWTYPE
INDEX BY PLS_INTEGER;
main_cur_tab main_cur_tabtype;
This gives you a flexible array that will automatically hold the columns selected in your cursor (main_cur).
You would collect the records into this array with the BULK COLLECT:
OPEN main_cur;
FETCH main_cur BULK COLLECT INTO main_cur_tab;
CLOSE main_cur;
and loop through them with:
FOR i IN main_cur_tab.FIRST .. main_cur_tab.LAST
LOOP
etc.
as for returning your resultset to Java, well I'm no java expert and you'll need to ask that in another forum or tag this question with the Java tag and hope a Java person picks it up and answers it for you.
Can you do this in SQL?
E.g. maybe using a WITH like this:
WITH q1 AS (SELECT ....)
SELECT /* q2 */ ... FROM q1
WHERE ..
GROUP BY etc
If you can do it in SQL you can always embed it in PL/SQL afterwards if you need to.
I have a table that consists of a unique id, and a few other attributes. It holds "schedules". Then I have another table that holds a list of all the times each schedule has or will "fire". This isn't the exact schema, but it's close:
create table schedule (
id varchar(40) primary key,
attr1 int,
attr2 varchar(20)
);
create table schedule_times (
id varchar(40) foreign key schedule(id),
fire_date date
);
I want to query the schedule table, getting the attributes and the next and previous fire_dates, in Java, sometimes ordering on one of the attributes, but sometimes ordering on either previous fire_date or the next fire_date. Ordering by the attributes is easy, I just stick an "order by" into the string while I'm building my prepared statement. I'm not even sure how to go about selecting the last fire_date and the next one in a single query - I know that I can find the next fire_date for a given id by doing a
SELECT min(fire_date)
FROM schedule_times
WHERE id = ? AND
fire_date > sysdate;
and the similar thing for previous fire_date using max() and fire_date < sysdate. I'm just drawing a blank on how to incorporate that into a single select from the schedule so I can get both next and previous fire_date in one shot, and also how to order by either of those attributes.
You can do that using two sub-queries in Left Joins.
This has the advantage of returning NULL for your fire_dates if there is no next/previous schedule.
Select id, attr1, attr2, next_fire_date, previous_fire_date
From schedule s
Left Join ( Select id, Min(fire_date) As next_fire_date
From schedule_times st
Where st.fire_date > Sysdate
Group By id ) n
On ( n.id = s.id )
Left Join ( Select id, Max(fire_date) As previous_fire_date
From schedule_times st
Where st.fire_date < Sysdate
Group By id ) p
On ( p.id = s.id )
You can then add your ORDER BY on next_fire_date or previous_fire_date.
If performance matters, create a compound index on schedule_times( id, fire_date ), this will allow the sub-queries to read only from this index.
Try something like this:
select schedule.*,
(
select max(si.fire_date) from schedule_times si where si.id = schedule.id and si.fire_date < sysdate
) as prevfire,
(
select min(si.fire_date) from schedule_times si where si.id = schedule.id and si.fire_date > sysdate
) as nextfire
from schedule
where id = ?
order by attr1
Modify the query to
SELECT Distinct S.ID, ATTR1, ATTR2,
LEAD(FIRE_DATE, 1, SYSDATE) OVER (PARTITION BY S.ID ORDER BY S.ID) NEXT_FIRE_DATE,
LAG(FIRE_DATE, 1, SYADATE) OVER (PARTITION BY S.ID ORDER BY S.ID) PREV_FIRE_DATE
FROM SCHEDULE S,SCHEDULE_TIMES ST WHERE ST.ID=S.ID;
THIS WAS SIMPLE QUERY. YOU CAN TRY THIS.
LEAD has the ability to compute an expression on the next rows (rows which are going to come after the current row) and return the value to the current row. The general syntax of LEAD is shown below:
LEAD (sql_expr, offset, default) OVER (analytic_clause)
sql_expr is the expression to compute from the leading row.
offset is the index of the leading row relative to the current
row. offset is a positive integer
with default 1.
default is the value to return if the offset points to a row
outside the partition range.
The syntax of LAG is similar except that the offset for LAG goes into the previous rows.