Write Java Code to execute SQL Group By - java

We would typically write Oracle SQL to find max salary of every employee in each department if we had a table with EmpID, DeptID, Salary:
select EmpID,DeptID, rank over(partition by DeptID order by Salary) rnk
from Table
where rnk=1;
OR
select EmpID
from Table1
where Salary =(Select max(Salary) from Table2 group by DeptID
and Table2.DeptId = Table1.DeptId )
If the above table was a file instead, then how can we write custom Java code to implement the same behavior?

If I got your question right, why don't you implement it by reading the file and passing it to the library of your choice for querying? Or by using Hibernate, and then passing the read file as:
Session.createSQLQuery(fromReadFile)
and
Session.createSQLQuery(fromReadFile).uniqueResults()
to get the results as a List or a particular object?

Related

How can I access a value when inserting into a table?

I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;

How to delete multiple rows from multiple tables using Where clause?

Using an Oracle DB, I need to select all the IDs from a table where a condition exists, then delete the rows from multiple tables where that ID exists. The pseudocode would be something like:
SELECT ID FROM TABLE1 WHERE AGE > ?
DELETE FROM TABLE1 WHERE ID = <all IDs received from SELECT>
DELETE FROM TABLE2 WHERE ID = <all IDs received from SELECT>
DELETE FROM TABLE3 WHERE ID = <all IDs received from SELECT>
What is the best and most efficient way to do this?
I was thinking something like the following, but wanted to know if there was a better way.
PreparedStatement selectStmt = conn.prepareStatment("SELECT ID FROM TABLE1 WHERE AGE > ?");
selectStmt.setInt(1, age);
ResultSet rs = selectStmt.executeQuery():
PreparedStatement delStmt1 = conn.prepareStatment("DELETE FROM TABLE1 WHERE ID = ?");
PreparedStatement delStmt2 = conn.prepareStatment("DELETE FROM TABLE2 WHERE ID = ?");
PreparedStatement delStmt3 = conn.prepareStatment("DELETE FROM TABLE3 WHERE ID = ?");
while(rs.next())
{
String id = rs.getString("ID");
delStmt1.setString(1, id);
delStmt1.addBatch();
delStmt2.setString(1, id);
delStmt2.addBatch();
delStmt3.setString(1, id);
delStmt3.addBatch();
}
delStmt1.executeBatch();
delStmt2.executeBatch();
delStmt3.executeBatch();
Is there a better/more efficient way?
You could do it with one DELETE statement if two of your 3 tables (for example "table2" and "table3") are child tables of the parent table (for example "table1") that have a "ON DELETE CASCADE" option.
This means that the two child tables have a column (example column "id" of "table2" and "table3") that has a foreign key constraint with "ON DELETE CASCADE" option that references the primary key column of the parent table (example column "id" of "table1"). This way only deleting from the parent table would automatically delete associated rows in the child tables.
Check out this in more detail : http://www.techonthenet.com/oracle/foreign_keys/foreign_delete.php
If you delete only few records of a large tables ensure that an index on the
column ID is defined.
To delete the records from the table TABLE2 and 3 the best strategy is to use the CASCADE DELETE as proposed by
#ivanzg - if this is not possible, see below.
To delete from TABLE1 a far superior option that a batch delete on a row basis, use signle delete using the age based predicate:
PreparedStatement stmt = con.prepareStatement("DELETE FROM TABLE1 WHERE age > ?")
stmt.setInt(1,60)
Integer rowCount = stmt.executeUpdate()
If you can't cascade delete, use for the table2 and 3 the same concept as above but with the following statment:
DELETE FROM TABLE2/*or 3*/ WHERE ID in (SELECT ID FROM TABLE1 WHERE age > ?)
General best practice - minimum logic in client, whole logic in the database server. The database should be able to do reasonable execution plan
- see the index note above.
DELETE statement operates a table per statement. However the main implementations support triggers or other mechanisms that perform subordinate modifications. For example Oracle's CREATE TRIGGER.
However developers might end up figuring out what is the database doing behind their backs. (When/Why to use Cascading in SQL Server?)
Alternatively, if you need to use an intermediate result in your delete statements. You might use a temporal table in your batch (as proposed here).
As a side note, I see not transaction control (setAutoCommit(false) ... commit() in your example code. I guess that might be for the sake of simplicity.
Also you are executing 3 different delete batches (one for each table) instead of one. That might negate the benefit of using PreparedStatement.

Complex INSERT query

I'm pretty new to MySQL. I have two related tables, quite common case: Klients(KID, name, surname) and Visits(VID, VKID, dateOfVisit) - VKID is the Klient ID. I have a problem with suitable INSERT query, this is what I want to do:
1.Check if Klient with specific name and surname exists (let's assume that there are no people with the same surnames)
2.If yes, get the ID and do the INSERT to Visits table
3.If no, INSERT new Klient, get the ID and INSERT to Visits.
Is it possible to do in one query?
You would need to use the IF EXIST / NOT EXISTS and use a subquery to check the table. See the reference bwlo
http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
HTH
The INSERT statement allows only one single target table.
So the query you're looking for is just impossible unless you use triggers or stored procedures.
But such problem is commonly solved using the fallowing small algorithm:
1) insert a record in table [Visits] assuming the parent record does exist in table [Klients]
INSERT INTO Visits (VKID, dateOfVisit)
SELECT KID, NOW()
FROM Klients
WHERE (name=#name) AND (surname=#surname)
2) check the number of inserted records after query (1)
3) if no record has been inserted, then add a new record table [Klients], and then run (1) again.
try something like this
IF (SELECT * FROM `sometable` WHERE name = 'somename' AND surname = 'somesurname') IS NULL THEN
INSERT INTO Table1(name,surname) VALUES ('somename', 'somesurname');
ELSE INSERT INTO visits(kid,name,surname)
SELECT kid, name, surname FROM Table1 WHERE name = 'somename' AND surname = 'somesurname';
END IF;
there is no need to specify 'VALUES' on the second insert
i have not tested it, but this is the general idea of what you are trying to accomplish.
These should be two queries in a transaction:
INSERT INTO Klients (name, surname)
VALUES ('John', 'Doe')
ON DUPLICATE KEY UPDATE
KID = LAST_INSERT_ID(KID);
INSERT INTO Visits (VKID, dateOfVisits)
VALUES (LAST_INSERT_ID(), NOW());
The first statement is an upsert statement where the update part uses not widely known, but intented exactly for the purpose functionality of LAST_INSERT_ID(), where explicitly passed value is stored for getting the value afterwards.
UPD: I forgot to mention that you would need to add a unique constraint on (surname, name).

Db2 merge from jdbc with dynamic values

I would like to use the db2 merge statement submitting it as a statement from jdbc.
I am in the following scenario. I'm working with a proprietary persistence layer and I'm handling an entity I don't know whether it's already persisted or not and I would like to use the merge statement in order to insert or update a row on the database.
Is it possible?
Suppose I'm working with the table people with three columns: id, name, surname and I'm handling an entity with id="5", name="chuck", surname="norris" Am I able to issue:
MERGE INTO people AS t
USING (select '5' as id, 'chuck' as name, 'norris' as surname from SYSIBM.SYSDUMMY1)As s
ON (t.id = s.id)
WHEN MATCHED THEN
UPDATE SET t.name=s.name, t.surmane=s.surname
WHEN NOT MATCHED THEN
INSERT
(id, name, surname)
VALUES (s.id, s.name, s.surname)
such a statement? I'm trying to do that but I got an error. I don't think it's allowed to use a select after USING:
USING (select '5' as id, 'chuck' as name, 'norris' as surname from SYSIBM.SYSDUMMY1)As s
I also tryed to do:
USING VALUES('5','chuck','norris') AS s(id,chuck,norris)
but it dosn't work. Any help would be appreciated.
Besides, does anybody know if it's possible to use such a statement in a prepared statement, replacing the real values expressed into the USING part with '?' placeholders in order to set them to the prepared statement using the setXXX() methods?
Thanks
Thanks
Fil
The syntax for MERGE for your data would be something like this, assuming you're using DB2 Linux/Unix/Windows (LUW). The VALUES clause goes inside the parenthesis for the USING part.
Also, if you are using LUW, you cannot dynamically prepare a MERGE (I.E., your query can't have parameter markers) in LUW 9.5 or less. This was added in LUW 9.7.
MERGE INTO people AS t USING (
VALUES (5, 'Chuck', 'Norris'),
(6, 'John', 'Smith'),
(7, 'Abraham', 'Lincoln')
-- maybe more rows
) AS s (id, name, surname)
ON t.id = s.id
WHEN MATCHED THEN
UPDATE SET t.name=s.name, t.surname=s.surname
WHEN NOT MATCHED THEN
INSERT (id, name, surname)
VALUES (s.id, s.name, s.surname)
However, your actual problem with the fullselect may be that you have some typos in your query... for example "surmane" in UPDATE SET t.name=s.name, t.surmane=s.surname

Dynamic search in SQL Server using java

I have an employee table like:
Empid EmpName Remark
001 Bob
002 Harish
003 Tom
004 Dicky
001 Bob
003 Tom
I have to find the duplicate employee id and accordingly updating the remark field as duplicate !
Thanks.
update employee set remark = 'duplicate'
where empid in (
select empid
from employee
group by empid, empname
having count(*) > 1 )
This question is very vague, because you do not mention what ORM library you are using or how you are accessing/manipulating your database. But basically want you want to do is execute a derived table query, then make a decision based on the results.
SELECT * FROM
(SELECT empId, count(empId) numIds from Employee group by empId) IdCount
WHERE numIds > 1;
Run this query via a PreparedStatement or whatever your ORM framework provides, then iterate over each result and update your remark field.
Below will give you ID of duplicate records
`select empID, empName, count(empID) as cnt from fschema.myTable group by (empID) having cnt > 1 order by cnt`
Will get back to you how to set remark as 1 for duplicates shortly...

Categories