MySQL Count that includes months not within the date range - java

Supposed I have a table that contains the following table:
+----+-----------+---------+-------------------------+
| id | item | .... | dateLogged |
+----+-----------+---------+-------------------------+
| 1 | 2015-001 | .... | 2015-06-01 12:09:00 |
| 2 | 2015-001 | .... | 2015-06-01 12:09:00 |
| 3 | 2015-002 | .... | 2015-06-30 15:40:02 |
| 4 | 2015-002 | .... | 2015-06-30 15:40:03 |
| 5 | 2015-003 | .... | 2015-07-02 13:38:22 |
+----+-----------+---------+-------------------------+
I want to search the number of items based on the year, month and count from the dateLogged, with the expected data:
+--------+----------+----------------+
| year | month | total |
+--------+----------+----------------+
| 2015 | January | 0 |
| 2015 | February | 0 |
| 2015 | March | 0 |
| 2015 | April | 0 |
| 2015 | May | 0 |
| 2015 | June | 4 |
| 2015 | July | 1 |
| 2015 | August | 0 |
| 2015 | September| 0 |
| 2015 | October | 0 |
| 2015 | November | 0 |
| 2015 | December | 0 |
+--------+----------+----------------+
But I am only getting this based on the following SQL:
SELECT DATE_FORMAT(dateLogged, '%Y') as 'year', MONTHNAME(dateLogged)
as 'month', COUNT(id) as 'total' from item a where dateLogged >= '2015-06-29 00:00:00'
and dateLogged <= '2015-07-02 23:59:59'
GROUP BY DATE_FORMAT(a.dateLogged, '%Y%m');
+--------+---------+----------------+
| year | month | No of items |
+--------+---------+----------------+
| 2015 | June | 4 |
| 2015 | July | 1 |
+----+-------------+----------------+
I've tried searching elsewhere from stackoverflow, but wasn't able to get any answers, or probably have missed. Wanna know if there is any solution to this, thanks.
I am using Java to compute this, so any suggestions using Java to compute the above is also welcomed.

You could change your query to return the 0 values as well using loops to iterate over months and years and then find the count. Something like this:
Map<String, Integer> values = new HashMap<String, Integer>();
for(int year = 2015; year < 2017; year++)
{
for(int month = 1; month <= 12; month++)
{
rs = stmt.executeQuery("SELECT COUNT(id) as 'total' from item where
MONTH(dateLogged) = " + month + " and YEAR(dateLogged) = " + year + ";";
rs.next();
int count = rs.getInt("total");
String time = Integer.toString(year) + "-" + Integer.toString(month);
values.Add(time, count);
}
}
This would return you a map like this:
values['2015-1'] = 0;
values['2015-2'] = 0;
.. and so on.
Of course, you could store the returned count with year and month however you want, depending on how you wish to use it further.

One idea is to, somehow, create a calendar table, and use it, so that you get a row for each month of year 2015:
SELECT DATE_FORMAT(m, '%Y') as 'year',
MONTHNAME(m) as 'month',
COUNT(id) as 'total'
FROM (SELECT '2015-01-01' AS m UNION ALL SELECT '2015-02-01' UNION ALL
SELECT '2015-03-01' UNION ALL SELECT '2015-04-01' UNION ALL
SELECT '2015-05-01' UNION ALL SELECT '2015-06-01' UNION ALL
SELECT '2015-07-01' UNION ALL SELECT '2015-01-08' UNION ALL
SELECT '2015-09-01' UNION ALL SELECT '2015-10-01' UNION ALL
SELECT '2015-11-01' UNION ALL SELECT '2015-12-01') AS x
LEFT JOIN item a
ON DATE_FORMAT(a.dateLogged, '%Y%m') = DATE_FORMAT(x.m, '%Y%m')
AND dateLogged >= '2015-06-29 00:00:00'
AND dateLogged <= '2015-07-02 23:59:59'
GROUP BY DATE_FORMAT(x.m, '%Y%m');
The above query makes use of sort of an inline calendar table for year 2015. This table is used as a basis in the FROM clause, so that the query returns a row for each of the months of year 2015.
Demo here

Related

Postgresql Generate_series. Generate dates by month

I am using select
SELECT
asl.id, asl.outstanding_principal as outstandingPrincipal, the_date as theDate, asl.interest_rate as interestRate, asl.interest_payment as interestPayment, asl.principal_payment as principalPayment,
asl.total_payment as totalPayment, asl.actual_delta as actualDelta, asl.outstanding_usd as outstandingUsd, asl.disbursement, asl.floating_index_rate as floatingIndexRate,
asl.upfront_fee as upfrontFee, asl.commitment_fee as commitmentFee, asl.other_fee as otherFee, asl.withholding_tax as withholdingTax, asl.default_fee as defaultFee,
asl.prepayment_fee as prepaymentFee, asl.total_out_flows as totalOutFlows, asl.net_flows as netFlows, asl.modified, asl.new_row as newRow, asl.interest_payment_modified as
interestPaymentModified, asl.date, asl.amortization_schedule_initial_id as amortizationScheduleInitialId, asl.tranche_id as trancheId, asl.user_id as userId, tr.local_currency_id as localCurrencyId,
f.facility_id
FROM
GENERATE_SERIES
(
(SELECT MIN(ams.date) FROM amortization_schedules ams),
(SELECT MAX(ams.date) + INTERVAL '1' MONTH FROM amortization_schedules ams),
'1 MONTH'
) AS tab (the_date)
FULL JOIN amortization_schedules asl on to_char(the_date, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')
LEFT JOIN tranches tr ON asl.tranche_id = tr.id
LEFT JOIN facilities f on tr.facility_id = f.id
In this select, I'm using generate_series to get each month since there are no records in the database for each month. But the matter is that this select gives me superfluous results. I use this select in my Spring Boot application. But the fact is that I need all the data, and for example only with a certain facility_id , and when I insert a condition
WHERE f.id = :id and tr.tranche_number_id = :trancheNumberId
My generate_series stops working (as I understand it, because I set certain conditions for generating a request) and instead of 30 lines, I get only 3.
How do I keep the ability to generate the theDate by month, with the ability to select specific IDs
I tried different options.
With this option:
FULL JOIN amortization_schedules asl on to_char(the_date, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 2 | 50000 | 2023-05-16 00:00:00.000000 |
| 3 | 0 | 2024-05-16 00:00:00.000000 |
In this case, it does not work correctly, since months are not generated and only three lines are displayed (if it is (the_date, 'yyyy-MM') = to_char(asl.date, 'yyyy-MM')).
If I change to (the_date, 'yyyy') = to_char(asl.date, 'yyyy') then the generation works, but it doesn't work correctly because it is year oriented.
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-07-16 00:00:00.000000 |
... ... ....
| 1 | 10000 | 2022-12-16 00:00:00.000000 |
| 2 | 50000 | 2023-01-16 00:00:00.000000 |
| 2 | 50000 | 2023-02-16 00:00:00.000000 |
| 2 | 50000 | 2023-03-16 00:00:00.000000 |
| 2 | 50000 | 2023-04-16 00:00:00.000000 |
... ... ....
| 3 | 0 | 2024-01-16 00:00:00.000000 |
but it should be:
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-07-16 00:00:00.000000 |
... ... ....
| 1 | 10000 | 2023-04-16 00:00:00.000000 |
| 2 | 50000 | 2023-05-16 00:00:00.000000 |
| 2 | 50000 | 2023-06-16 00:00:00.000000 |
| 2 | 50000 | 2023-07-16 00:00:00.000000 |
| 2 | 50000 | 2023-08-16 00:00:00.000000 |
... ... ....
| 3 | 0 | 2024-05-16 00:00:00.000000 |
| 3 | 0 | 2024-06-16 00:00:00.000000 |
| 3 | 0 | 2024-07-16 00:00:00.000000 |
I'm making a few intuitive leaps here, so if something looks off it might be because I don't have the entire picture.
From what I can tell you want the amortization schedule starting from the "date" for each ID and then going out a specific amount of time. I am guessing it is not truly the max date in that entire table, and that it varies by ID. In your example you went out one year, so for now I'm going with that.
You can use a generate_series inline, which will explode out each row. I believe something like this will give you the output you seek:
with schedule as (
select
id,
generate_series (date, date + interval '1 year', interval '1 month')::date as dt
from
amortization_schedules
)
select
asl.id, s.dt, asl.outstanding_principal
from
amortization_schedules asl
join schedule s on asl.id = s.id
JOIN tranches tr ON asl.tranche_id = tr.id
JOIN facilities f on tr.facility_id = f.id
WHERE
f.id = :id and
tr.tranche_number_id = :trancheNumberId
Is there another field that tells, by id, when the payments should end or one that will let us derive it (number of payments, payment end date, etc)?
One final note. If you use [left] outer joins and a where clause, as in below:
LEFT JOIN tranches tr ON asl.tranche_id = tr.id
LEFT JOIN facilities f on tr.facility_id = f.id
WHERE
f.id = :id and
tr.tranche_number_id = :trancheNumberId
You have effectively nullified the "left" and made these inner joins. In this case, get rid of "left," not because it will return wrong results but because it misleads. You are saying those fields must have those specific values, which means they must first exist. That's an inner join.
If you truly wanted these as left joins, this would have been more appropriate, but I don't think this is what you meant:
LEFT JOIN tranches tr ON
asl.tranche_id = tr.id and
tr.tranche_number_id = :trancheNumberId
LEFT JOIN facilities f on
tr.facility_id = f.id and
f.id = :id

Join two entries rows ( Start time and End time) as a single row in Talend

I have Data coming from a MS SQL Database, it is concerning the the working hours of employees.
The problem is that, the start time and the end time are stored as 2 different entries, so when the employee comes, he scans his badge and this is considered arrival time, and when he leaves, he scans his badge again and this is considered departure time.
There is one column that helps to make the difference between the start and the end time (CodeNr column : B1 = StartTime, B2 = EndTime)
so this is how my Table looks like
Now i need this data as a single entry, in Talend oder from the Database,
so that should looks like
What to use in order to achieve this please (specially in Talend and when to complicate than in MS SQL)?
CREATE TABLE EmployeeWorkLoad(
EmployeeNr bigint,
Year int,
Month int,
Day int,
Hour int,
Minute int,
CodeNr char(2)
)
Insert into [EmployeeWorkLoad] ( [EmployeeNr],[Year],[Month] ,[Day],[Hour], [Minute] ,[CodeNr]) Values (1,2020,1,4,8,30,'B1'),
(1,2020,1,4,16,45,'B2'),
(1,2020,1,6,8,15,'B1'),
(1,2020,1,6,16,45,'B2'),
(2,2020,3,2,8,10,'B1'),
(2,2020,3,2,16,5,'B2')
GO
6 rows affected
WITH CTE AS (
select EmployeeNr,Year,Month,Day,
MAX(CASE WHEN CodeNr='B1' THEN Hour END) AS StartHour,
MAX(CASE WHEN CodeNr = 'B1' THEN Minute END) AS StartMinute,
MAX(CASE WHEN CodeNr = 'B2' THEN Hour END) AS EndHour,
MAX(CASE WHEN CodeNr = 'B2' THEN Minute END) AS EndMinute
from EmployeeWorkLoad
group by EmployeeNr,Year,Month,Day )
SELECT * , ABS(EndHour-StartHour) AS DutationHour
,ABS(IIF(EndMinute <StartMinute, EndMinute+60, EndMinute)- StartMinute) AS DurationMinute
FROM
CTE
GO
EmployeeNr | Year | Month | Day | StartHour | StartMinute | EndHour | EndMinute | DutationHour | DurationMinute
---------: | ---: | ----: | --: | --------: | ----------: | ------: | --------: | -----------: | -------------:
1 | 2020 | 1 | 4 | 8 | 30 | 16 | 45 | 8 | 15
1 | 2020 | 1 | 6 | 8 | 15 | 16 | 45 | 8 | 30
2 | 2020 | 3 | 2 | 8 | 10 | 16 | 5 | 8 | 55
db<>fiddle here

To calculate a moving average for a list of stock data prices in an Oracle DB - is it faster using SQL or Java?

I have an Oracle Database with a table containing asset price data for which I want to calculate a 10-day moving average in a separate column.
Is it faster to use SQL for this or should I load the data into a Java Hashmap/ ArrayList first, do the calculation and transfer the results it back to the Oracle DB afterwards?
The Table looks like this:
| ASSET_ID | PRICE | DATE | MA |
-----------------------------------------
| 43 | 33.12 | 2018-09-17 | 33.05 |
| 43 | 34.02 | 2018-09-18 | 33.07 |
| 43 | 30.22 | 2018-09-19 | 33.01 |
| 43 | 31.52 | 2018-09-20 | 32.85 |
Use AVG( PRICE ) OVER ( PARTITION BY asset_id ORDER BY "DATE" RANGE BETWEEN 10 PRECEDING AND 0 FOLLOWING ) to get a moving average:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( ASSET_ID, PRICE, "DATE", MA ) AS
SELECT 43, 33.12, DATE '2018-09-17', CAST( NULL AS NUMBER(8,2) ) FROM DUAL UNION ALL
SELECT 43, 34.02, DATE '2018-09-18', NULL FROM DUAL UNION ALL
SELECT 43, 30.22, DATE '2018-09-19', NULL FROM DUAL UNION ALL
SELECT 43, 31.52, DATE '2018-09-20', NULL FROM DUAL UNION ALL
SELECT 43, 32.52, DATE '2018-09-21', NULL FROM DUAL UNION ALL
SELECT 43, 33.52, DATE '2018-09-22', NULL FROM DUAL UNION ALL
SELECT 43, 34.52, DATE '2018-09-23', NULL FROM DUAL UNION ALL
SELECT 43, 35.52, DATE '2018-09-24', NULL FROM DUAL UNION ALL
SELECT 43, 36.52, DATE '2018-09-25', NULL FROM DUAL UNION ALL
SELECT 43, 37.52, DATE '2018-09-26', NULL FROM DUAL UNION ALL
SELECT 43, 38.52, DATE '2018-09-27', NULL FROM DUAL UNION ALL
SELECT 43, 39.52, DATE '2018-09-28', NULL FROM DUAL UNION ALL
SELECT 43, 40.52, DATE '2018-09-29', NULL FROM DUAL UNION ALL
SELECT 43, 41.52, DATE '2018-09-30', NULL FROM DUAL;
Query 1:
MERGE INTO table_name dst
USING (
SELECT ROWID rid,
ROUND(
AVG( price ) OVER (
PARTITION BY asset_id
ORDER BY "DATE"
RANGE BETWEEN 10 PRECEDING AND 0 FOLLOWING
),
2
) AS new_MA
FROM table_name
) src
ON ( dst.ROWID = src.rid )
WHEN MATCHED THEN
UPDATE SET MA = src.new_MA
Results:
14 Rows Updated
Query 2:
SELECT *
FROM table_name
Results:
| ASSET_ID | PRICE | DATE | MA |
|----------|-------|----------------------|-------|
| 43 | 33.12 | 2018-09-17T00:00:00Z | 33.12 |
| 43 | 34.02 | 2018-09-18T00:00:00Z | 33.57 |
| 43 | 30.22 | 2018-09-19T00:00:00Z | 32.45 |
| 43 | 31.52 | 2018-09-20T00:00:00Z | 32.22 |
| 43 | 32.52 | 2018-09-21T00:00:00Z | 32.28 |
| 43 | 33.52 | 2018-09-22T00:00:00Z | 32.49 |
| 43 | 34.52 | 2018-09-23T00:00:00Z | 32.78 |
| 43 | 35.52 | 2018-09-24T00:00:00Z | 33.12 |
| 43 | 36.52 | 2018-09-25T00:00:00Z | 33.5 |
| 43 | 37.52 | 2018-09-26T00:00:00Z | 33.9 |
| 43 | 38.52 | 2018-09-27T00:00:00Z | 34.32 |
| 43 | 39.52 | 2018-09-28T00:00:00Z | 34.9 |
| 43 | 40.52 | 2018-09-29T00:00:00Z | 35.49 |
| 43 | 41.52 | 2018-09-30T00:00:00Z | 36.52 |

Calculate/Determine Hours for Nightshift in mysql

Here is the table for the employee's logs:
And what I want is to generate the time ins and time out of employees. like this:
Can anyone help me for this? Any added logic or algorithm will be accepted.
This is one way. And it will work for day/night any shifts, provided, the first min(datetime) represent IN.
Rextester Sample
select t.enno
,max(datetime) as time_out
,min(datetime) as time_in
,time_to_sec(timediff(max(datetime), min(datetime) )) / 3600
as No_of_hours
from
(
SELECT
floor(#row1 := #row1 + 0.5) as day,
t.*
FROM Table4356 t,
(SELECT #row1 := 0.5) r1
order by t.datetime
) t
group by t.day,t.enno
;
Output
+------+---------------------+---------------------+-------------+
| enno | time_out | time_in | No_of_hours |
+------+---------------------+---------------------+-------------+
| 6 | 16.05.2017 06:30:50 | 15.05.2017 18:30:50 | 12,0000 |
| 6 | 17.05.2017 05:30:50 | 16.05.2017 18:10:50 | 11,3333 |
+------+---------------------+---------------------+-------------+
Explanation:
SELECT
floor(#row1 := #row1 + 0.5) as day,
t.*
FROM Table4356 t,
(SELECT #row1 := 0.5) r1
order by t.datetime
This query uses sequence to increment #row1 with 0.5, so you will get 1 1.5 2 2.5. Now if you just get the integer part of with with floor, you will generate sequece like 1 1 2 2. So this query will give you this output
+-----+------+---------------------+
| day | enno | datetime |
+-----+------+---------------------+
| 1 | 6 | 15.05.2017 18:30:50 |
| 1 | 6 | 16.05.2017 06:30:50 |
| 2 | 6 | 16.05.2017 18:10:50 |
| 2 | 6 | 17.05.2017 05:30:50 |
+-----+------+---------------------+
Now you can group by day and get max and min time.

Select data from specific year

I need a solution for my problem here.
I got 2 tables, assetdetail and assetcondition. Here is the structure of those tables.
assetdetail
-----------------------------------------------------------
| sequenceindex | assetcode | assetname | acquisitionyear |
-----------------------------------------------------------
| 1 | 110 | Car | 2012-06-30 |
| 2 | 111 | Bus | 2013-02-12 |
assetcondition
--------------------------------------------------------------------------
|sequenceindex | indexassetdetail | fiscalyear | assetamount | assetprice |
---------------------------------------------------------------------------
| 1 | 1 | 2012 | 1 | 20000000 |
| 2 | 1 | 2013 | 1 | 15000000 |
| 3 | 2 | 2013 | 1 | 25000000 |
And i want the result is like this:
------------------------
assetname | assetprice |
------------------------
Car | 20000000 |
Bus | 25000000 |
Note: using "SELECT WHERE fiscalyear = "
Without explaining how your tables are linked one can only guess. Here's the query I came up with.
select assetdetail.assetname,
sum( assetcondition.assetprice )
from assetdetail
inner join assetcondition
on assetcondition.indexassetdetail = assetdetail.sequenceindex
where assetcondition.fiscalyear = 2013
group by assetdetail.assetname;
I haven't understand from a logical point of view your query. By the way the operator that you have to you use is the JOIN's one.
The SQL that follows, I don't know if it is what you want.
Select assetname, assetprice
From assetdetail as ad join assetcondition as ac on (as.sequenceindex = ac.sequenceindex)
Where fiscalyear = '2013'
Not quite sure if it is what you're looking for, but I guess what you want is a JOIN:
SELECT
assetdetail.assetname, assetcondition.assetprice
FROM
assetdetail
JOIN
assetcondition
ON
assetdetail.sequenceindex = assetcondition.sequenceindex
WHERE
YEAR(acquisitionyear) = '2013'

Categories