Grouping by value with separation of the output - java

I have a this type of data:
| company_id | role_id |
| 1 | 1 |
| 2 | 2 |
| 1 | 2 |
Here's my query which selects one company id and passed as an argument role id.
SELECT company_id AS companyId, COUNT(role_id) AS usersNumber
FROM companies
WHERE role_id = :userId
GROUP BY companyId
Where :userId is an argument passed from JPA.
Which produces something like this:
| company_id | passed_role_id_count |
| 1 | 1 |
| 2 | 1 |
What I want to achieve is to have the output like this:
| company_id | first_role_count | second_role_count |
| 1 | 1 | 1 |
| 2 | 0 | 1 |
Is it possible to have the output like this? Or maybe is it better to select all from DB and then group using Java Streams?

Use a filtered aggregation:
SELECT company_id AS companyId,
COUNT(*) filter (where role_id = 1) AS first_role_count,
COUNT(*) filter (where role_id = 2) AS second_role_count
FROM companies
WHERE role_id = :userId
GROUP BY companyId

With the hint from #jarlh I managed to solve my problem:
SELECT eu.company_id,
SUM(CASE WHEN eu.role_id = 2 THEN 1 ELSE 0 END) AS "first_role",
SUM(CASE WHEN eu.role_id = 3 THEN 1 ELSE 0 END) AS "second_role"
FROM users eu
GROUP BY eu.company_id;

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

Is it possible to use sequence generator in this situation?

I have below table structure
ITEM
| ID(Auto Inc) | ORG_ID(FK to ORG) | ITEM_ID |
|-----------------|----------------------|-----------------------|
| 1 | 1 | 1 (Initial Val for A) |
| 1 | 2 | 1 (Initial Val for B) |
| 1 | 1 | 2 (Incremented for A) |
ORG
| ID | NAME |
|------|-----------|
| 1 | A |
| 2 | B |
Is there any possibility of using any generator to manage item_id column. This is not id column for ITEM table. Business requirement is to manage item_id sequential for each org.
You may try to insert using the next query:
INSERT INTO item (org_id, item_id)
SELECT #org_id, COALESCE((SELECT 1 + MAX(item_id)
FROM item
WHERE org_id = #org_id), 1)
where #org_id is the value to be inserted.
The problem: it may insert duplicates while concurrent insertions occures.

Oracle query to filter and sort table based on values in child table

I am using Oracle 11g, and I have tables with data and structure as follows:
TABLE1_PARENT:
-------------------
PID | Name | Age |
-------------------
1 | Mark | 35 |
2 | Jane | 40 |
3 | Agatha | 45 |
-------------------
TABLE2_CHILD
==============================================
CID | Name | Age | Class | House | PID |
----------------------------------------------
1 | John | 7 | 3 | Red | 1 |
2 | Marie | 5 | 1 | Yellow| 2 |
3 | Petra | 6 | 2 | Green | 3 |
4 | Taylor | 8 | 4 | Blue | 2 |
5 | Lean | 9 | 5 | Red | 2 |
6 | Justin | 7 | 3 | Yellow| 3 |
7 | Arianna | 5 | 1 | Blue | 3 |
8 | Brendon | 6 | 2 | Green | 3 |
9 | Shawn | 7 | 3 | Red | 1 |
----------------------------------------------
For a single condition, the query is simple:
SELECT * FROM TABLE1_PARENT WHERE PID IN (SELECT PID FROM TABLE2_CHILD WHERE AGE=7);
which will result in the following result:
TABLE1_PARENT:
-------------------
PID | Name | Age |
-------------------
1 | Mark | 35 |
3 | Agatha | 45 |
-------------------
However, if I want to fetch the list of parents whose children are of age=7 and belong to house='GREEN', if I write a query as below:
SELECT * FROM TABLE1_PARENT WHERE PID IN (SELECT PID FROM TABLE2_CHILD WHERE AGE=7 AND house='GREEN');
I would get no results.
I am expecting the result to be :
TABLE1_PARENT:
-------------------
PID | Name | Age |
-------------------
3 | Agatha | 45 |
-------------------
since Agatha has a child belonging to age=7, and a child belonging to house='GREEN'.
I was able to come up with a solution for a similar data structure using Java streams. I am trying to do the same using Oracle SQL.
List<Parent> filteredParents = parents.stream()
.filter(parent -> parent.getChildren().stream()
.anyMatch(child -> child.getAge().equals("7")) && parent.getChildren().stream()
.anyMatch(child -> child.getHouse().equalsIgnoreCase("Green")))
.collect(Collectors.toList());
I am expecting the query to give me a result where the conditions could match any of the children. Because, the filtering is happening at the Parent level.
Any help would be great. Thanks!
You can do it with EXISTS:
SELECT t.*
FROM TABLE1_PARENT t
WHERE t.PID IN (
SELECT PID FROM TABLE2_CHILD WHERE AGE=7
) AND EXISTS (
SELECT 1 FROM TABLE2_CHILD WHERE PID = t.PID AND House = 'Green'
)
The problem with your query is that it tries to apply both conditions in the same row inside TABLE2_CHILD. But this is not what you want.
You want the parent ids for which children rows have AGE = 7 and there is a row inside TABLE2_CHILD with House = 'Green'.
You could use EXISTS for both conditions, which may be more efficient:
SELECT t.*
FROM TABLE1_PARENT t
WHERE EXISTS (
SELECT 1 FROM TABLE2_CHILD WHERE PID = t.PID AND AGE=7
) AND EXISTS (
SELECT 1 FROM TABLE2_CHILD WHERE PID = t.PID AND House = 'Green'
)
or with conditional aggregation by grouping the child table by PID:
SELECT * FROM TABLE1_PARENT
WHERE PID IN (
SELECT PID
FROM TABLE2_CHILD
GROUP BY PID
HAVING
SUM(CASE WHEN Age = 7 THEN 1 ELSE 0 END) > 0
AND
SUM(CASE WHEN House = 'Green' THEN 1 ELSE 0 END) > 0
)
You are not getting output because in your TABLE2_CHILD, there is no child whose age is 7 in the green house. For your expected result, the query should be:
SELECT * FROM TABLE1_PARENT WHERE PID IN (SELECT PID FROM TABLE2_CHILD WHERE AGE=7) AND PID IN (SELECT PID FROM TABLE2_CHILD WHERE house='GREEN');
If you want to get the parent who have a child with age = 7, and the same child belongs to house=GREEN, use the query you already wrote:
SELECT * FROM TABLE1_PARENT WHERE PID IN (SELECT PID FROM TABLE2_CHILD IF AGE=7 AND house='GREEN');
If you want to get the parent whose children either has age=7 or house=GREEN or both, use
SELECT * FROM TABLE1_PARENT WHERE PID IN (SELECT PID FROM TABLE2_CHILD IF AGE=7 OR house='GREEN');

How to select next record and previous record in SQLite?

I have been searching like forever
I am using min and max for the last and and first record but how do I get the next/ record? I have a column name rowid it is the pk and auto incremented by one every time a user registers
| rowid | Name |
| 1 | John |*
| 2 | Mark |
| 3 | Peter|
| 4 | Help |
so if I click the next button I wanted to select mark which is in rowid 2
| rowid | Name |
| 1 | John |
| 2 | Mark |*
| 3 | Peter|
| 4 | Help |
but if I click the next button twice I want to be in rowid 4
| rowid | Name |
| 1 | John |
| 2 | Mark |
| 3 | Peter|
| 4 | Help |*
how do I do that? by the way I don't have fixed rows since I have a registration function
so here's my code
JButton btnNextLast = new JButton(">>");
btnNextLast.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try{
String sQuery = "select * from accountinfo where rowid = (SELECT MAX(rowid) FROM accountinfo)";
PreparedStatement prst = connection.prepareStatement(sQuery);
ResultSet rs = prst.executeQuery();
lblSID.setText(rs.getString("sid"));
lblfirst.setText(rs.getString("last"));
lblLast.setText(rs.getString("first"));
lblmiddle.setText(rs.getString("middle"));
lblbirth.setText(rs.getString("birth"));
lblcontact.setText(rs.getString("contact"));
}catch(Exception qwe){
}
}
});
I've tried
select rowid
from accountinfo
where rowid >1
order by rowid
limit 1
but no luck
and if I remove order by rowid limit 1. It just show the next record which is 2 and never function again

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