How to use the query builder of Symfony to make a date range counter and fill the gaps with zeros - doctrine

I have a query that count the user by grouping them by sign up date.
return $this->createQueryBuilder('s')
->select(' date(s.created_at) as x, count(1) as y')
->where("s.created_at between datesub(now(), :months, 'Month') and now()")
->setParameter('months', $months)
->groupBy('x')
->orderBy('x')
->getQuery()
->getResult();
But their is currently gaps in my dataset.
So I have the sql request to fill the gaps, but I don't know how to create a complicated request with the Symfony's query builder.
SELECT ranger.ranger_date AS x, COALESCE(counter.counter_value, 0) as y
FROM (
SELECT DATE(s.created_at) AS counter_date, count(*) AS counter_value
FROM statistic AS s
WHERE s.created_at between DATE_SUB(NOW(), INTERVAL 3 MONTH) and now()
GROUP BY counter_date
) AS counter
RIGHT JOIN (
SELECT DATE(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY)) AS ranger_date
FROM (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)units
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)tens
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)hundreds
WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 3 MONTH) AND NOW()
) AS ranger
ON ranger.ranger_date = counter.counter_date
ORDER BY ranger.ranger_date
I have already tried with the createQuery method, but it did not work...

If your complex native sql query is successfully returning the result set you want:
You can simply prepare and execute the query as documented by Symfony.
If you need to hydrate entities then you can use the NativeQuery class.

Related

Multiply with Previous Value from One colum in Oracle SQL

I have the following result, which is easily calculated in Excel, but how to do it in Oracle, the result is the following, based on a previous select and comes from one column,
Result from select Expected result
1.62590
0.60989 0.991620151
0.83859 0.831562742
the result is based on 1.62590 * 0.60989 = 0.991620151,
1.62590 * 0.60989 * 0.83859 = 0.831562742
You can use:
SELECT id,
result,
EXP(SUM(LN(result)) OVER (ORDER BY id)) AS expected
FROM table_name;
Note: Use any other column instead of id to give the appropriate ordering or, if your rows are already ordered, use the ROWNUM pseudo-column instad of id.
Which, for the sample data:
CREATE TABLE table_name (id, Result) AS
SELECT 1, 1.62590 FROM DUAL UNION ALL
SELECT 2, 0.60989 FROM DUAL UNION ALL
SELECT 3, 0.83859 FROM DUAL;
Outputs:
ID
RESULT
EXPECTED
1
1.6259
1.62590000000000000000000000000000000001
2
.60989
.9916201510000000000000000000000000000026
3
.83859
.8315627424270900000000000000000000000085
fiddle
One option is to use a recursive CTE; it, though, expects that sample data can be sorted, somehow, so I added the ID column which starts with 1, while other values are incremented by 1:
Sample data:
SQL> with
2 test (id, col) as
3 (select 1, 1.62590 from dual union all
4 select 2, 0.60989 from dual union all
5 select 3, 0.83859 from dual
6 ),
Query begins here:
7 product (id, col, prod) as
8 (select id, col, col
9 from test
10 where id = 1
11 union all
12 select t.id, t.col, t.col * p.prod
13 from test t join product p on p.id + 1 = t.id
14 )
15 select id,
16 round(prod, 10) result
17 from product;
ID RESULT
---------- ----------
1 1,6259
2 ,991620151
3 ,831562742
SQL>
You can use a MODEL clause:
SELECT *
FROM (SELECT ROW_NUMBER() OVER (ORDER BY id) AS rn, result FROM table_name)
MODEL
DIMENSION BY (rn)
MEASURES ( result, 0 AS expected)
RULES (
expected[rn] = result[cv()] * COALESCE(expected[cv()-1], 1)
)
order by rn;
Which, for the sample data:
CREATE TABLE table_name (id, Result) AS
SELECT 1, 1.62590 FROM DUAL UNION ALL
SELECT 2, 0.60989 FROM DUAL UNION ALL
SELECT 3, 0.83859 FROM DUAL;
Outputs:
RN
RESULT
EXPECTED
1
1.6259
1.6259
2
.60989
.991620151
3
.83859
.83156274242709
fiddle

Duplicated rows numbering

I need to number the rows so that the row number with the same ID is the same. For example:
Oracle database. Any ideas?
Use the DENSE_RANK analytic function:
SELECT DENSE_RANK() OVER (ORDER BY id) AS row_number,
id
FROM your_table
Which, for the sample data:
CREATE TABLE your_table ( id ) AS
SELECT 86325 FROM DUAL UNION ALL
SELECT 86325 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86352 FROM DUAL UNION ALL
SELECT 86353 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL;
Outputs:
ROW_NUMBER
ID
1
86325
1
86325
2
86326
2
86326
3
86352
4
86353
5
86354
5
86354
db<>fiddle here

How to breakdown data by month and showing zero for months with no data?

Using information in Table A, how can I produce results in Table B below?
Table A:
CASE_ID DATE_EFF COPAY STATUS
1 11/04/2016 10 A
1 11/20/2016 5 A
1 11/23/2016 5 R
1 12/01/2016 1 A
1 12/10/2016 2 A
1 12/12/2016 10 A
1 12/31/2016 50 R
For the above CASE_ID, we have dates in Nov 2016 and Dec 2016 only, however, I want to produce a breakdown of this CASE_ID for a period of 6 months as below where for each month the copays are summed where applicable as per the DATE_EFF and for the months that are not within the above dates, a zero is entered. Also, only records with copays with a status of 'A' are summed for any month -- so those with status of 'R' are ignored in the summation. For example, based on data in Table A above, the intended results are as follow:
Table B:
CASE_ID MONTH TOTAL_COPAY
1 01/2017 0
1 12/2016 13
1 11/2016 15
1 10/2016 0
1 09/2016 0
1 08/2016 0
I have below as a possible solution[using a with clause], but can this be achieved without the use of the 'with' clause?
Possible Solution:
WITH
XRF AS
( SELECT CASE_ID, COPAY, DATE_EFF
FROM Table_A WHERE STATUS = 'A'
)
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM XRF F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;
UPDATE AND SOLUTION:
Using the above query, I believe I am have answered my own question by implementing it as below -- not sure though if using this method is expensive when you have millions of records of such CASE_IDs. Any thoughts?
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM (SELECT CASE_ID, COPAY, DATE_EFF FROM TABLE_A WHERE STATUS = 'A') F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;

Oracle SQL Select Query Getting Max Row As a Fraction of a Rollup Total

hoping I might be able to get some advise regarding Oracle SQL…
I have a table roughly as follows (there are more columns, but not necessary for this example)…
LOCATION USER VALUE
1 1 10
1 2 20
1 3 30
2 4 10
2 5 10
2 6 20
1 60
2 40
100
I’ve used rollup to get subtotals.
What I need to do is get the max(value) row for each location and express the max(value) as a percentage or fraction of the subtotal for each location
ie:
LOCATION USER FRAC
1 3 0.5
2 6 0.5
I could probably solve this using my limited knowledge of select queries, but am guessing there must be a fairly quick and slick method..
Thanks in advance :)
Solution using analytic functions
(Please note the WITH MY_TABLE AS serving only as dummy datasource)
WITH MY_TABLE AS
( SELECT 1 AS LOC_ID,1 AS USER_ID, 10 AS VAL FROM DUAL
UNION
SELECT 1,2,20 FROM DUAL
UNION
SELECT 1,3,30 FROM DUAL
UNION
SELECT 2,4,10 FROM DUAL
UNION
SELECT 2,5,10 FROM DUAL
UNION
SELECT 2,6,20 FROM DUAL
)
SELECT LOC_ID,
USER_ID,
RATIO_IN_LOC
FROM
(SELECT LOC_ID,
USER_ID,
RATIO_IN_LOC,
RANK() OVER (PARTITION BY LOC_ID ORDER BY RATIO_IN_LOC DESC) AS ORDER_IN_LOC
FROM
(SELECT LOC_ID,
USER_ID,
VAL,
VAL/SUM(VAL) OVER (PARTITION BY LOC_ID) AS RATIO_IN_LOC
FROM MY_TABLE
)
)
WHERE ORDER_IN_LOC = 1
ORDER BY LOC_ID,
USER_ID;
Result
LOC_ID USER_ID RATIO_IN_LOC
1 3 0,5
2 6 0,5
with inputs ( location, person, value ) as (
select 1, 1, 10 from dual union all
select 1, 2, 20 from dual union all
select 1, 3, 30 from dual union all
select 2, 4, 10 from dual union all
select 2, 5, 10 from dual union all
select 2, 6, 20 from dual
),
prep ( location, person, value, m_value, total ) as (
select location, person, value,
max(value) over (partition by location),
sum(value) over (partition by location)
from inputs
)
select location, person, round(value/total, 2) as frac
from prep
where value = m_value;
Notes: Your table exists already? Then skip everything from "inputs" to the comma; your query should begin with with prep (...) as ( ...
I changed user to person since user is a keyword in Oracle, you shouldn't use it for table or column names (actually you can't unless you use double quotes, which is a very poor practice).
The query will output two or three or more rows per location if there are ties at the top. Presumably this is what you desire.
Output:
LOCATION PERSON FRAC
---------- ---------- ----------
1 3 .5
2 6 .5

Select Maximum record

Here is my Table EMP_EARN_DETAILS.
Emp_Ern_No is the primary key.
I need to get the amount for each emp_no for each earn_no where the emp_earn_no is the maximum.
The output should be as follows.
0004321 ERN001 2345 11
0004321 ERN002 345 10
0004321 ERN003 345 9
000507 ER-01 563 4
000732 ERN001 2345 12
000732 ERN002 9 13
000732 ERN003 678 8
Please help me with the query
You can aggregate by the fields you need and, at the same time, order by the EMP_EARN_NO value; this can be a solution, by analytic functions:
WITH TEST(emp_no, earn_no, amount, emp_earn_no) AS
(
SELECT '0004321' , 'ERN001' ,2345 ,11 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN002' ,345 , 10 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN003' ,345 ,9 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,56 ,1 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 , 2 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,3 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,4 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,123 ,7 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,2345 ,12 FROM DUAL UNION ALL
SELECT '00732' , 'ERN002' ,9 ,13 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,67 ,5 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,456 ,6 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,678 ,8 FROM DUAL
)
SELECT emp_no, earn_no, amount, emp_earn_no
FROM (
SELECT emp_no,
earn_no,
amount,
emp_earn_no, ROW_NUMBER() OVER ( PARTITION BY EMP_NO, EARN_NO ORDER BY emp_earn_no DESC) AS ROW_NUM
FROM TEST
)
WHERE ROW_NUM = 1
Give this a shot,
SELECT EMP_NO, SUM(AMOUNT)
FROM EMP_EARN_DETAILS
GROUP BY EMP_NO
HAVING EMP_EARN_NO = MAX(EMP_EARN_NO)
Try this query:
select emp_no, earn_no,
sum(amount) keep (dense_rank last order by emp_earn_no) as sum_amount
from emp_earn_details
group by emp_no, earn_no
First by following query , your conditions achieved :
select t.emp_no a ,t.earn_no b ,max(t.amount) c
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
Only things that you must specify , in a same record with different EMP_EARN_NO. You have to specify in same record which must be in result.
So if you want maximum EMP_EARN_NO be in result you can use following query as final query (exactly your target in question):
select t.emp_no a ,t.earn_no b ,max(t.amount) c, max(t.emp_earn_no) emp_earn_no
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
If you want minimum or others EMP_EARN_NO be in result you can above query replace max function by your conditions.

Resources