How can I update with one query?
I want to do something like this:
update customer
set balance = (400,150) where customer_id IN ('2','3');
customer 2 will get a new balance of 400 and customer 3 will get 150.
I want 1 query because I'm using spring-boot, JPA
#Modifying
#Query("update customer set balance = (400,150) where customer_id IN ('2','3');")
Can I do here 2 queries? for each customer?
what is recommended? what is acceptable?
thanks.
You can do by this way -
Update customer
SET balance = (case when customer_id = '2' then '400'
when customer_id = '3' then '150'
end)
WHERE
customer_id IN ('2','3');
The CASE statement may be what you are looking for.
UPDATE customer
SET balance = (case
when customer_id = 1 then 150
when customer_id = 2 then 300
end)
WHERE ID in (1,2);
If your customer_id is of type string, add quotes to the customer_id numbers.
My example is just a modified version of:
Example Code:
UPDATE students
SET JavaScore = (case
when ID = 1 then 75
when ID = 2 then 80
when ID = 3 then 86
when ID = 4 then 55
end),
PythonScore = (case
when ID = 1 then 70
when ID = 2 then 85
when ID = 3 then 94
when ID = 4 then 75
end)
WHERE ID in (1,2,3,4);
From this website:
DelftStack
Hibernate can do this for you, no need to write your own query.
The steps.
Set hibernate.jdbc.batch_size to some reasonable size.
Enable insert/update query ordering
Enable statement rewrites for MySQL (set rewriteBatchedStatements to true)
In your application.properties add the following
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
# spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true # Needed when using versioned data
spring.datasource.hikari.dataSourcePoperties.rewriteBatchedStatements=true
See also this and this for a bit more background.
Now in your code you can just update and save your customers and the database will receive only 1 query.
if you want to use the spring-data way, you have to use complex SQL/JPQL as less as possible.
#Entity
class CustomerEntity {
}
#Modifying
#Query("update CustomerEntity customer set customer.balance = :balance where customer.id = :customerId")
int updateCustomerBalance(#Param("customerId") String customerId, #Param("balance") String balance);
customerRepository.updateCustomerBalance("2", "400");
customerRepository.updateCustomerBalance("3", "150");
Common transaction
if you want to update happens in one transaction
#Transactional
void doUpdate() {
customerRepository.updateCustomerBalance("2", "400");
customerRepository.updateCustomerBalance("3", "150");
}
Keep in mind that you have to call service.doUpdate() from outside. if you call the method from another service method, transaction will not be created.
Check that update has happened
int count = customerRepository.updateCustomerBalance("2", "400");
if (count == 0) {
log.error("Customer not updated customerId=2 customerBalance=400");
}
Related
I am facing an issue in Oracle Query to achieve the following use case,
Consider I have two tables :
Table 1 : product
productId - Integer - primaryKey
productName - Varchar
Table 2 : product_sequence
productId - Integer - primaryKey
sequenceId - Integer - primaryKey
orderId - Integer
orderName - Varchar
product table has 1000 entries and product_sequence table has 10K entries
Requirement :
(paginate) fetch the entries from 0 to 100 / 100 to 200 / etc., in the product table
Distinct count of productId for showing the pagination in UI (check the sample query below)
Filter by 'productName' in 'product' table and 'orderName' in 'product_sequence' table
Query (tried) :
SELECT
p.productId, p.productName, ps.orderId, ps.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM (
select *
from product
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) p
JOIN product_sequence ps on p.productId=ps.productId
WHERE ps.orderId IN ('12','13','14');
NOTE : the above query will work in Oracle, But the issue is
Expected:
Return 100 entries from 'product' table with mapped entries in the 'product_sequence' table
Actual :
It first LIMITS 100 entries in product and then filter the orderId so the number of entries returned is reduced from 100 to lesser number
I agree my query is not correct: It first LIMIT by 100 in 'product' table in subquery and then goes for filter in second table which reduces the count
Could some one help me with the query for this please? Anyhelp is appreciated.
If my question is not clear, Let me know, I can explain with more info.
Try to move the OFFSET and FETCH clauses to the outer query, something like this:
SELECT q.productId, q.productName, q.orderId, q.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM ( SELECT * FROM product p JOIN product_sequence ps ON p.productId = ps.productId
WHERE ps.orderId IN ('12','13','14') ) q
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
To get 100 rows per page "after filtering" you'll need to find all the productid values first, then process the main query.
For example:
select
p.productid, p.productname, ps.orderid, ps.orderName,
count(distinct p.productid) over() as total
from product p
join product_sequence ps on p.productid = ps.productid
where ps.orderid in ('12','13','14')
and p.productid in (
select *
from (
select distinct p.productid
from product p
join product_sequence ps on p.productid = ps.productid
where p.productname like '%theremin%' -- filter #1
and ps.orderid in ('12','13','14') -- filter #2
) x
order by productid
offset 300 rows -- get the 4th page
fetch first 100 rows only -- page size set to 100 rows
)
See example at db<>fiddle.
I have a list which is a java object like below.
public class TaxIdentifier {
public String id;
public String gender;
public String childId;
public String grade,
public String isProcessed;
////...///
getters and setters
///....///
}
Records in DB looks like below,
id gender childId grader isProcessed
11 M 111 3 Y
12 M 121 4 Y
11 M 131 2 Y
13 M 141 5 Y
14 M 151 1 Y
15 M 161 6 Y
List<TaxIdentifier> taxIdentifierList = new ArrayList<TaxIdentifier>();
for (TaxIdentifier taxIdentifier : taxIdentifierList) {
}
while I process for loop and get the id = 11, i have to check if there are other records with id = 11 and process them together and do a DB operation and then take the next record say in this case 12 and see if there are other records with id = 12 and so on.
One option is i get the id and query the DB to return all id = 11 and so on.
But this is too much back and forth with the DB.
What is the best way to do the same in java? Please advice.
If you anyway need to process all the records in the corresponding database table - you should retrieve all of them in 1 database roundtrip.
After that, you can collect all your TaxIdentifier records in dictionary data structure and process in whatever way you want.
The brief example may look like this:
Map<String, List<TaxIdentifier>> result = repositoty.findAll().stream().collect(Collectors.groupingBy(TaxIdentifier::getId));
Here all the TaxIdentifier records are grouped by TaxIdentifier's id (all the records with id equals "11") can be retrieved and processed this way:
List<TaxIdentifier> taxIdentifiersWithId11 = result.get("11");
I would leverage the power of your database. When you query, you should order your query by id in ascending order. Not sure which database you are using but I would do:
SELECT * FROM MY_DATABASE WHERE IS_PROCESSED = 'N' ORDER BY ID ASC;
That takes the load of sorting it off of your application and onto your database. Then your query returns unprocessed records with the lowest id's on top. Then just sequentially work through them in order.
I want to write a time check. That is 1 customer can't deposit more than 5 times 1 days. I just wrote this to check:
SELECT CUSTOMERID
FROM TRANSACTIONS
WHERE (SELECT DATEPART(HOUR,GETDATE())) BETWEEN ((SELECT DATEPART(HOUR,GETDATE()))-24) AND (SELECT DATEPART(HOUR,GETDATE()))
AND METHODID = 1
AND CUSTOMERID = 8
To count customer and check if >5 return false. But i think it's wrong. Anyone help me the query about hour and date ( Column Date is DATETIME type)
Here the image of my table.
As per my understanding, You have a customer ID and wanna check the number of transaction in a day of that customer and allow it for the transaction if count is less than 5. So try the following query.(Query will return "true" if transactions<5 else "false").
select case when count(*) < 5 then 'True' else 'False' end from [YOUR_TABLE_NAME]
where CUSTOMERID = 8
and cast([YOUR_DATE_COLUMN_NAME] as date)=cast(GETDATE() as date)
You need aggregate function count, group by clause and case when for checking
If your dbms is Mysql then below query will help you
select customerid,date(date) as date_of_month,
case when count(transactionid)>5 then 'false' else 'True'
from TRANSACTIONS
group by customerid,date(date)
If your dbms is mssql server
select customerid,convert(date,[date]) as date_of_month,
case when count(transactionid)>5 then 'false' else 'True'
from TRANSACTIONS
group by customerid,convert(date,[date])
I'm using MySQL and fetching a few different values from a table and then perform some basic math on it. Currently three seperate SELECT statements are in use and afterwards I perform some simple addition and subtraction with the outputs I get in Java.
I'm trying to optimize my code but sadly I gotta admit I'm a complete SQL noob. I'm pretty sure there's a way to join these select querys and the calculations so that I actually only get one output but I've not been able to find it.
My table looks something like this:
ID | value | inc | timestamp
--------------------------------------
0 | 5 | 4 | 2018-02-01 10:28:21
1 | 8 | 3 | 2018-02-01 10:28:47
...
My code currently looks like this:
int maxValue = MySQL.executeQuery("SELECT MAX(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())");
int minValue = MySQL.executeQuery("SELECT MIN(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())");
int minInc = MySQL.executeQuery("SELECT `inc` FROM `table` where ID = id AND value = minValue");
int output = maxValue - minValue + minInc;
Is there a way to shorten it to a single
int output = MYSQL.executeQuery( ??? );
?
Simply do select (select ...) - (select ...) + (select ...)
In your case, you can do( not tested in real environment )
select (SELECT MAX(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())) - ( SELECT MIN(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())) + (SELECT `inc` FROM `table` where ID = id AND value = minValue)
First off, there's something funky going on with the maxValue and minValue selects. The Max() and Min() operators will give you the max and min values of a given column of a given set of rows. Using one of these operators with such a specific where (by what seems to be a table's primary key) is probably not what you want to be doing.
Now, answering your question, I think you could do something along the lines of:
SELECT MAX('value') as max, MIN('value') as min
FROM `table` as t
WHERE ...
to "join" (careful with this word) the first 2 queries. This is simple select syntax: usually, there's no problem with selecting more than a column or an aggregate function at a time. Or, something like:
SELECT `inc`
FROM `table`
WHERE ID = id AND
value = (SELECT MIN('value') FROM 'table' WHERE ...)
to "join" the last two.
Single statement is possible using INNER JOIN since you are using a single able. Try this
SELECT MAX(`a.value`)-(MIN(`a.value`)+b.`inc`) AS Output
FROM `table` a
INNER JOIN `table` b ON a.ID=b.ID
AND a.ID = idvalue AND `a.timestamp` >= TIMESTAMPADD(a.DAY,-3,NOW())
AND b.value=(select MIN(value) from table WHERE ID=id);
I am trying to create a nested SELECT SQL statment. I store all value and id and want to select rows that satisfy multiple values. How can I generate a SELECT statement using Java? For example,
ID VALUE
1 RED
2 BIG
1 SMALL
1 SMOOTH
2 TALL
.....
To select an item that is both red and small the statement would be:
SELECT *
FROM table
WHERE table.value = RED AND
id IN (SELECT *
FROM table
WHERE table.value = SMALL AND id IN (...))
This type of problem is called Relational Division
SELECT ID
FROM tableName
WHERE VALUE IN ('SMALL','SMOOTH')
GROUP BY ID
HAVING COUNT(*) = 2
SQLFiddle Demo
the query above will result 1 since the ID contains both records.
If no unique constraint was enforce on value for every ID, DISTINCT is required.
SELECT ID
FROM tableName
WHERE VALUE IN ('SMALL','SMOOTH')
GROUP BY ID
HAVING COUNT(DISTINCT VALUE) = 2
SQLFiddle Demo
SQLFiddle Demo (with duplicate)
OTHER(s)
SQL of Relational Division
select ID
from MyTable
where VALUE in ('RED', 'SMALL')
group by ID
having count(distinct VALUE) = 2
SQL Fiddle Example
Results:
| ID |
------
| 1 |
Here is a general way to approach this:
select id
from t
group by id
having max(case when value = 'Red' then 1 else 0 end) = 1 and
max(case when value = 'Small' then 1 else 0 end) = 1
In other words, membership in the set becomes a clause in the having statement. These can be both inclusion and exclusion (use = 0 instead of = 1) and optional (use or instead of and).