How to get the last 3 distinct ids from a mysql table - java

Ok so basically I have my database table. The first column is the id. The second is a pkg_id. The 3rd is not important and the 4th is the previous id that the pkg_id was located at. I need to pull the last 3 pkg_id's from the table. So basically I need to pull the last 3 17879 pkg_id's and the last 3 3075. So in this example I need to pull id 9 , 7 , 6 for 17879 and id 8, 5, 3 for 3075.
I can't get my head around it. I do have access to the previous id that it was. So you see that for id 9 it says that 17879 was last in id 7. That id 8 was last in id 5.
If anybody could help me write a query that would be great. I'm also using Java for database access so it doesn't have to be just in mysql. Thanks so much.

SELECT m.*
FROM (
SELECT pkg_id,
COALESCE(
(
SELECT id
FROM mytable mi
WHERE mi.pkg_id = md.pkg_id
ORDER BY
id DESC
LIMIT 2, 1
), 0) AS mid
FROM (
SELECT DISTINCT pkg_id
FROM mytable
) md
) q
JOIN mytable m
ON m.pkg_id <= q.pkg_id
AND m.pkg_id >= q.pkg_id
AND m.id >= q.mid
Create an index on mytable (pkg_id, id) for this to work fast.
Note this condition: m.pkg_id <= q.pkg_id AND m.pkg_id >= q.pkg_id instead of mere m.pkg_id = q.pkg_id. This is required for the index to be used efficiently.

Related

JPA Union Equivelant Need for Adding Constant to Query Result

I need union equivelant for Union. I know we can do this by joining selections but i couldn't do that for adding constant to query results. Here is my sql
SELECT MIN(t1.EXAMPLE_NUMBER)
FROM
(
SELECT 1 AS EXAMPLE_NUMBER
UNION ALL
SELECT EXAMPLE_NUMBER + 1
FROM selection.SELECTION
) t1
LEFT OUTER JOIN selection.SELECTION t2
ON t1.EXAMPLE_NUMBER = t2.EXAMPLE_NUMBER
WHERE t2.EXAMPLE_NUMBER IS NULL;
This query need for finding minimum unused integer for the column. Lets say:
EXAMPLE_NUMBER
1
4
5
I need to get 2 as a result for this case. That is the sql for this. So here is my question:
I use QueryDsl-JPA for this. Since JPA 2, i can't use UNION. So i can't do this sql with querydsl-jpa, i was thinking about go like this:
JPAQuery baseQuery = new JPAQuery(em);
SubQueryExpression handleNumberOne = baseQuery.select(Expressions.constant(1));
SubQueryExpression selectAvailableMinNumber = baseQuery.select(selection.exampleNumber.add(1)).from(selection);
baseQuery.union(handleNumberOne, selectAvailableMinNumber); // NO UNION AVAILABLE
Is it any available way to do this with querydsl-JPA? I don't want to include querydsl-sql library just for this reason, I'm looking for JPA style solution. What i tried so for to try adding constant (1 in my case) to result of select query without union. By this way I may able to continue. Any suggestion?
You do not have to generate all the numbers to get this information.
For all numbers except 1, you can do:
select s.example_number + 1
from selection s
where not exists (select 1
from selection s2
where s2.example_number = s.example_number + 1
)
fetch first 1 row only; -- or limit 1
If you also want to get "1" if it doesn't exist, then:
select (case when min_en > 1 then 1
else s.example_number + 1
end)
from selection s cross join
(select min(example_number) as min_en from selection) mins
where not exists (select 1
from selection s2
where s2.example_number = s.example_number + 1
)
fetch first 1 row only; -- or limit 1

MySql count of rows and insert into the table that count

I have a scenario where i need to take count of rows in mysql table for the current branch(in that table we are store branch) and insert the count of rows with other details into the same table. But the problem is when two or more concurrent users try to insert from the same branch at the same time the count is same for all the users, but for me the insert should not happ for the other user(s) until i read the count and insert that one user request . Is there any way the locking works for this and any example would be helpful(All i need to do this in MySql store procedure)
Edit : Sorry, I cant share the working code but i can write example here
My table structure is here
id name branchid count
1 abc 1 1
2 xyz 1 2
3 abcd 2 1
4 wxyz 2 2
Here am taking count of rows from the above table for given branch(ex : 1) and inserting the row with that calculated count
Ex :
set #count = (select count(id) from tbl where branchid = 1);
later
insert into tbl(id, name, branchid, count)
values(5, 'abcd', 1, #count)
This works great provided if only one user access this from one branch , but if more than one user from same branch try to access this at exact same time the
#count
is duplicating for the branch users.
Why not just do it in one query:
insert into tbl(id, name, branchid, count)
select 5, 'abcd', 1, count(*)
from from tbl
where branchid = 1;

Roll back whole query if condition is not met in a single row SQL

I have to perform an update on a USERPRODUCTS table
PRODUCTID USERNAME PRODUCTCOUNTRY
1 A US
2 A UK
3 B US
4 B CA
5 B US
6 C IR
7 F IR
8 A IR
I have another table PERSONCOUNTRY where I know which person can have which country product license only e.g.
PERSON COUNTRYFORPRODUCTLICENSE
A UK
B US
C IR
Now what I want to do is to write a single query to update the table USERPRODUCTS and SET person A to person B such that the query should update only if B has COUNTRYFORPRODUCTLICENSE for ALL rows in USERPRODUCTS for person A otherwise should not update ANY row.
e.g. Person A has PRODUCTCOUNTRY UK, US and IR from above table but B has COUNTRYFORPRODUCTLICENSE only US. So, update query should update row 1 (because B has license for country US) but when it reaches row 2, it should ALL roll back, even the first row it updated.
I know this cannot be handled through single update
UPDATE USERPRODUCTS userproducts
SET USERNAME = 'B'
WHERE USERNAME = 'A'
AND EXISTS (SELECT * FROM PERSONCOUNTRY personcountry where USERPRODUCTS.PRODUCTCOUNTRY IN (US))
This query will update only where A has US in PRODUCTCOUNTRY but will not roll back.
What is the fastest way to do it ? I wanted to do it in a single update query but cannot. Can we use CURSOR to do it ? but I have to do it in Hibernate code
I still think you can solve this with a not exists. From your comments I get the feeling you're thinking row based, but sql works based on sets. When you execute an update statement it doesn't update a row and move to the next, it updates all rows that fit the where clause.
So something along these lines:
UPDATE USERPRODUCTS
SET USERNAME = 'B'
WHERE USERNAME = 'A'
AND EXISTS (
SELECT NULL
FROM PERSONCOUNTRY PC
WHERE PC.COUNTRYFORPRODUCTLICENSE IN ('US')
AND PC.PERSON = USERPRODUCTS.USERNAME
)
AND NOT EXISTS (
SELECT NULL
FROM PERSONCOUNTRY PC
WHERE PC.COUNTRYFORPRODUCTLICENSE NOT IN ('US')
AND PC.PERSON = USERPRODUCTS.USERNAME
)

How can I create rows for dates missing from a range?

I am trying to get data for all dates in a range provided by my query, but I'm only getting the dates that actually exist in my table - missing dates are not reported. I need to create records in the table for those missing dates, with other columns left null, and then include them in the results.
My table table_name has records like:
ID Name Date_only
---- ---- -----------
1234 xyz 01-Jan-2014
1234 xyz 02-Jan-2014
1234 xyz 04-Jan-2014
...
For example, for the range 01-Jan-2014 to 04-Jan-2014, my query is:
select * from table_name
where id=1234
and (date_only >= '01-Jan-14' and date_only <= '04-Jan-14')
From Java or queried directly this shows three rows, with no data for 03-Jan-2014.
I need a single statement to insert rows for any missing dates into the table and return the data for all four rows. How can I do that?
UPDATE
Followed query worked for only if only 1 record available in table OR search range 2-5 days,
SELECT LEVEL, to_date('2014-11-08','yyyy-mm-dd') + level as day_as_date FROM DUAL CONNECT BY LEVEL <= 10 .
UPDATE WITH FIDDLE EXAMPLE
I got Error is:
I have table data and same query executed then i got error is ORA-02393: exceeded call limit on CPU usage, fiddle example is : my owntable sqlfiddle example .thanks in advance
you can use the below SQL for your purpose.The sql fiddle here http://sqlfiddle.com/#!4/3ee61/27
with start_and_end_dates as (select min(onlydate) min_date
,max(onlydate) max_date
from mytable
where id='1001'
and onlydate >= to_date('01-Jan-2015','dd-Mon-YYYY')
and onlydate <= to_date('04-Jan-2015','dd-Mon-YYYY')),
missing_dates as (select min_date + level-1 as date_value
from start_and_end_dates connect by level <=(max_date - min_date) + 1)
select distinct id,name,date_value
from mytable,missing_dates
where id='1001'
order by date_value
EDIT1:- Using your other example.The sqlfiddle is http://sqlfiddle.com/#!4/4c727/16
with start_and_end_dates as (select min(onlydate) min_date
,max(onlydate) max_date
from mytable
where name='ABCD'),
missing_dates as (select min_date + level-1 as date_value
from start_and_end_dates connect by level <=(max_date - min_date) + 1)
select distinct id,name,date_value
from mytable,missing_dates
where name='ABCD'
order by date_value;
You can use a query like
SELECT LEVEL, to_date('2014-01-01','yyyy-mm-dd') + level as day_as_date
FROM DUAL
CONNECT BY LEVEL <= 1000
to get a list of 1000 days from Jan 1 2014 (adjust to your need)
Next do an insert from select
INSERT INTO table_name (date_only)
SELECT day_as_date FROM (<<THE_QUERY_ABOVE>>)
WHERE day_as_date NOT IN (SELECT date_only FROM table_name)

Auto Increment including Year and Month in MySql

I have a table called > Project with an auto increment field for project estimate bid number called Project_ID.
This field is auto incremented. I have created it as an 8 digit character field which carries the field rule.
I need it to auto increment as a two number year, two number month, include a hyphen, and then a number starting at 001 for first record in that time period.
An example for the month of April 2012 would be 1204-001 for the first
record, 1204-002 for the 2nd and etc. then when the month of May rolls
around the Project_ID would change to 1205-001.
What I’ve been trying to write is as follows, I kept it as a simple default expression with a default value of
Cyear(date()) + (month()) + “-“ + “001” .
How I have Achieve this?
Basically, you can use BEFORE INSERT TRIGGER on the table you want the column to be incremented.
Here are some steps to create a simple algorithm and put this code inside the trigger:
// get current YEAR
SET #cur_Year = CONCAT(DATE_FORMAT(CURDATE(), '%Y'));
// get current MONTH
SET #cur_MONTH = CONCAT(DATE_FORMAT(CURDATE(), '%m'));
// concatenate YEAR and MONTH
SET #Year_Month = CONCAT(#cur_Year, #cur_MONTH);
// get the last value for the current YEAR and MONTH
SET #max_ID = ( SELECT MAX(ID)
FROM tableName
WHERE ID LIKE CONCAT(#Year_Month, '-%'));
// get the last three characters from the id, convert in to
// integer and increment by 1
SET #last_ID = CAST(RIGHT(#max_ID, 3) AS SIGNED) + 1;
// pad zero on the left using LPAD and
// concatenate it with YEAR and MONTH
SET #new_ID = CONCAT(#Year_Month,'-',LPAD(CAST(#last_ID AS CHAR(3)), 3, '0'));
INSERT INTO (Project_ID, col1, col2, col3)
SELECT DATE_FORMAT(NOW(), CONCAT('%y%m-',
(( SELECT RIGHT(CONCAT('000', (RIGHT(Project_ID, 3) + 1)), 3) AS number
FROM table_name
WHERE LEFT(Project_ID, 5) = DATE_FORMAT(NOW(), '%y%m-'))
ORDER BY Project_ID DESC
UNION
( SELECT '001')
LIMIT 1))),
'Col1 data', 'Col2 data', 'Col3 data'
This might look a bit odd, so I'll just explain the flow:
I use INSERT INTO ... SELECT so that I can check existing data from table_name to see if there are any existing cases already. The WHERE will find existing cases, and thanks to both RIGHT and LEFT it isn't too hard to carve out relevant data needed. If no rows are found, '001' is used instead, then you simply assign the existing columns as shown.
I have absolutely solved it,just take a look..
At first you have to take a sample table where the columns will be same to the columns of your original table except the column project_id.
then first insert a row in the original table where the value of column project_id=0 and the other columns are null,just insert the first row manually like this.
Then create a trigger on the sample table like the following...
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
delimiter ;;
create trigger t after insert on try
for each row
begin
declare v int;
declare c int;
set v=(select max(project_id) from original);
if(v=0)then
insert into original set
project_id=concat((select concat(right(substring_index((select * from(select curdate() from try limit 1) as a),'-','1'),2),right(substring_index((select * from(select curdate() from try limit 1) as a),'-','2'),2)) from try limit 1),'-001'),
project=new.project;
else
set c=(select right((select max(project_id) from original)as x,3) from original limit 1);
insert into original set
project_id=concat((select concat(right(substring_index((select * from(select curdate() from try limit 1) as a),'-','1'),2),right(substring_index((select * from(select curdate() from try limit 1) as a),'-','2'),2)) from try limit 1),concat('-00',c+1)),
project=new.project;
delete from original limit 1;
end if;
end;;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
In the above trigger my sample table was try(project text) and the original table was original(project_id text,project text).
After creating a trigger like this on the sample table,start inserting rows in the sample table,the rows will automatically inserted in the original table with auto_increment values in the project_id column like..1405-001,1405-002,1405-003.... where 14 is 2014 and 05 is May and the rest are auto_incremented values which is being incremented using the trigger.
Just follow the above steps, your problem will be surely solved.

Categories