Doctrine 2 - How to search with DQL and grouping - doctrine

I can't figure out how i can combine my LIKE search with grouping it by date and a category in DQL..
Explanation:
I have a search function that search my events after weight.. it looks like this:
SELECT e as event,
(CASE
(e.name LIKE :searchterm) THEN 10
ELSE 0
END) +
(CASE
WHEN (e.name LIKE :searchtermFull) THEN 5
ELSE 0
END)
AS weight
FROM
\Entities\Event e
WHERE
e.name LIKE :searchterm OR
e.name LIKE :searchtermFull
ORDER BY weight DESC
This works fine, but now i want to have these searchresults grouped by year and date (not time) so my items is grouped like this:
27 jan:
event 1
event 2
28 jan:
event 3
event 4
And i also need to do is to get it grouped my categories so i in the end will have:
27 jan:
Category 1
Event 1
Event 2
Category 2
Event 3
My Database event table:
name varchar(255)
description varchar(255)
created datetime
place_id int(11)
eventdate datetime
organiser_id int(11)
created_user_id int(11)
category int(11)
My Database category table
id int(11)
name varchar(255)
description varchar(255)
I have a Event Entity and a EventCategory Entity and what the EventCategory does is just to bind the category id on to the Event Entity
--Update - my sql so far--
SELECT
c.name,
YEAR(e.eventdate) as year,
MONTH(e.eventdate) as month,
DAY(e.eventdate) as day,
count(*) as total
FROM `events` as e, `eventCategories` as c
WHERE
e.category = c.id
GROUP BY
YEAR(e.eventdate),
MONTH(e.eventdate),
DAY(e.eventdate)
ORDER BY eventdate ASC
The DoctrineExtensions is included by writing this in the autoloader:
$classLoader = new Doctrine\Common\ClassLoader('DoctrineExtensions', ROOT.DS."library");
$classLoader->register();

You don't need to write your own function or install third party library. Since datetime is stored as string, you can use Doctrine's function SUBSTRING() to extract certain part and group by it.
For example:
SELECT SUBSTRING(e.eventdate, 1, 4) AS year FROM … GROUP BY year;

You need to use date functions DAY, MONTH or YEAR (Mysql)
Doctrine as is does not support these functions so you need to write your own (DQL functions) or install this library
Group records by year, month
WHERE ... GROUP BY YEAR(eventdate), MONTH(eventdate) ORDER BY eventdate ASC
Group records by year, month and day
WHERE ... GROUP BY YEAR(eventdate), MONTH(eventdate), DAY(eventdate) ORDER BY eventdate ASC

Related

exclude part of the select not to consider date where clause

i have a select(water readings, previous water reading, other columns) , a "where clause" that is based on date water reading date. however for previous water reading it must not consider the where clause. I want to get previous meter reading regardless where clause date range.
looked at union problem is that i have to use the same clause,
SELECT
WATERREADINGS.name,
WATERREADINGS.date,
LAG( WATERREADINGS.meter_reading,1,NULL) OVER(
PARTITION BY WATERREADINGS.meter_id,WATERREADINGS.register_id
ORDER BY WATERREADINGS.meter_id DESC,WATERREADINGS.register_id
DESC,WATERREADINGS.readingdate ASC,WATERREADINGS.created ASC
) AS prev_water_reading,
FROM WATERREADINGS
WHERE waterreadings.waterreadingdate BETWEEN '24-JUN-19' AND
'24-AUG-19' and isactive = 'Y'
The prev_water_reading value must not be restricted by the date BETWEEN '24-JUN-19' AND '24-AUG-19' predicate but the rest of the sql should be.
You can do this by first finding the previous meter readings for all rows and then filtering those results on the date, e.g.:
WITH meter_readings AS (SELECT waterreadings.name,
waterreadings.date dt,
lag(waterreadings.meter_reading, 1, NULL) OVER (PARTITION BY waterreadings.meter_id, waterreadings.register_id
ORDER BY waterreadings.readingdate ASC, waterreadings.created ASC)
AS prev_water_reading,
FROM waterreadings
WHERE isactive = 'Y')
-- the meter_readings subquery above gets all rows and finds their previous meter reading.
-- the main query below then applies the date restriction to the rows from the meter_readings subquery.
SELECT name,
date,
prev_water_reading,
FROM meter_readings
WHERE dt BETWEEN to_date('24/06/2019', 'dd/mm/yyyy') AND to_date('24/08/2019', 'dd/mm/yyyy');
Perform the LAG in an inner query that is not filtered by dates and then filter by the dates in the outer query:
SELECT name,
"date",
prev_water_reading
FROM (
SELECT name,
"date",
LAG( meter_reading,1,NULL) OVER(
PARTITION BY meter_id, register_id
ORDER BY meter_id DESC, register_id DESC, readingdate ASC, created ASC
) AS prev_water_reading,
waterreadingdate --
FROM WATERREADINGS
WHERE isactive = 'Y'
)
WHERE waterreadingdate BETWEEN DATE '2019-06-24' AND DATE '2019-08-24'
You should also not use strings for dates (that require an implicit cast using the NLS_DATE_FORMAT session parameter, which can be changed by any user in their own session) and use date literals DATE '2019-06-24' or an explicit cast TO_DATE( '24-JUN-19', 'DD-MON-RR' ).
You also do not need to reference the table name for every column when there is only a single table as this clutters up your code and makes it difficult to read and DATE is a keyword so you either need to wrap it in double quotes to use it as a column name (which makes the column name case sensitive) or should use a different name for your column.
I've added a subquery with previous result without filter and then joined it with the main table with filters:
SELECT
WATERREADINGS.name,
WATERREADINGS.date,
w_lag.prev_water_reading
FROM
WATERREADINGS,
(SELECT name, date, LAG( WATERREADINGS.meter_reading,1,NULL) OVER(
PARTITION BY WATERREADINGS.meter_id,WATERREADINGS.register_id
ORDER BY WATERREADINGS.meter_id DESC,WATERREADINGS.register_id
DESC,WATERREADINGS.readingdate ASC,WATERREADINGS.created ASC
) AS prev_water_reading
FROM WATERREADINGS) w_lag
WHERE waterreadings.waterreadingsdate BETWEEN '24-JUN-19' AND '24-AUG-19' and isactive = 'Y'
and WATERREADINGS.name = w_lag.name
and WATERREADINGS.date = w_lag.date

Oracle 'Partition By' and 'Row_Number' keyword along with pivot

I have this query written by someone else and I am trying to figure out how is it working. I have general idea about all these things such as row_number() , partition by, pivot but I am unable to understand them all together.
For this query :
select
d, p, s, a
from
(
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name)) as rownumber from occupations
)
pivot
(
max(name)
for occupation
in ('Doctor' as d, 'Professor' as p, 'Singer' as s, 'Actor' as a)
)
order by rownumber;
This is the input table on which the above query works :
This it the output generated by the query which is correct as per the question :
Jenny Ashley Meera Jane
Samantha Christeen Priya Julia
NULL Ketty NULL Maria
Now, I want to know how the output is generated by the query i.e. step by step with flow of execution. Explanation with easy examples matching the above situation would be much appreciated. Thanks in advance.
After from clause you have following :
select name,occupation, (ROW_NUMBER() OVER (partition by occupation order by name))
Above virtually restack your table data in three columns - Name, occupation, rownumber. rownumber will reset itself as soon as occupation column changes. Output data will be like :
NAME OCCUPATION ROWNUMBER
-------------------- -------------------- ----------
Jane ACTOR 1
Julia ACTOR 2
Maria ACTOR 3
JENNY DOCTOR 1 <-- rownumber reset to 1
Sammantha DOCTOR 2
Pivot function let you aggregate result & rotate rows into columns.
Pivot usage code is :
PIVOT
(
aggregate_function(column2)
FOR column2
IN ( expr1, expr2, ... expr_n) | subquery
)
So your PIVOT function have name stacked NAME based on OCCUPATION . Each stack (column in output) is ordered by rownumber column inserted via first subquery.

How to replace start date and end date for multiple row data with the maximum and minimum of date in pl/sql

I have a multiple rows data with unique start date and end date combination.
I want to replace the start date with the minimum of all the start dates
and End date with maximum of all the end dates.
Example:
id start Date end Date
1005 09/01/2000 05/31/2001
1005 09/05/2000 05/23/2001
100775 03/15/2005 04/30/2005
100775 03/25/2005 04/22/2005
3273 09/01/2003 12/31/2004
3273 09/11/2003 12/11/2004
Now I want the output to look like:
id start Date end date
1005 09/01/2000 05/31/2001
1005 09/01/2000 05/31/2001
100775 03/15/2005 04/30/2005
100775 03/15/2005 04/30/2005
3273 09/01/2003 12/31/2004
3273 09/01/2003 12/31/2004
I have tried doing this with max and min functions but that doesn't work since I want this to be only one query for using it in oracle report builder.
You can use just use min and max function to get your desired output:
select
id,
min(start_date) OVER (PARTITION BY id) start_date,
max(end_date) OVER (PARTITION BY id) end_date
FROM table1;
SQL Fiddle Demo
Another way of doing this,
UPDATE T1 SET StartDate = MinStartDate, EndDate = MaxEndDate
FROM (
Select id, min(StartDate) as 'MinStartDate',
Max(EndDate) as 'MaxEndDate'
from T1
Group by id
) AS T2
WHERE T1.id = T2.id;

Oracle sequence in a queue table

So I have a table called rentalqueue(queue_id,customer_id,movie_title,etc.) I also have a sequence on the queue_id as such:
CREATE SEQUENCE rentalqueue_seq
MINVALUE 100
MAXVALUE 300
START WITH 100
INCREMENT BY 1
NOCACHE;
Lets say I insert several movie titles into rentalqueue for a given customer from a movie table. How can I implement a trigger(assumption), so that I can designate the very last movie added in the queue? Assume I add 5 movies over lets say a couple days, each with a different timestamp.
I suggest you modify your sequence so that the maxvalue is something like 99999999. You can select the last movie added for a customer like this:
select *
from (
select *
from rentalqueue
where customer_id = :p_customer_id
order by queue_id desc)
where rownum = 1
Or, if you have a date_added field (or something similar), like this:
select *
from (
select *
from rentalqueue
where customer_id = :p_customer_id
order by date_added desc)
where rownum = 1

Grouping data by date ranges

I wonder how do I select a range of data depending on the date range?
I have these data in my payment table in format dd/mm/yyyy
Id Date Amount
1 4/1/2011 300
2 10/1/2011 200
3 27/1/2011 100
4 4/2/2011 300
5 22/2/2011 400
6 1/3/2011 500
7 1/1/2012 600
The closing date is on the 27 of every month. so I would like to group all the data from 27 till 26 of next month into a group.
Meaning to say I would like the output as this.
Group 1
1 4/1/2011 300
2 10/1/2011 200
Group 2
1 27/1/2011 100
2 4/2/2011 300
3 22/2/2011 400
Group 3
1 1/3/2011 500
Group 4
1 1/1/2012 600
It's not clear the context of your qestion. Are you querying a database?
If this is the case, you are asking about datetime but it seems you have a column in string format.
First of all, convert your data in datetime data type (or some equivalent, what db engine are you using?), and then use a grouping criteria like this:
GROUP BY datepart(month, dateadd(day, -26, [datefield])), DATEPART(year, dateadd(day, -26, [datefield]))
EDIT:
So, you are in Linq?
Different language, same logic:
.GroupBy(x => DateTime
.ParseExact(x.Date, "dd/mm/yyyy", CultureInfo.InvariantCulture) //Supposed your date field of string data type
.AddDays(-26)
.ToString("yyyyMM"));
If you are going to do this frequently, it would be worth investing in a table that assigns a unique identifier to each month and the start and end dates:
CREATE TABLE MonthEndings
(
MonthID INTEGER NOT NULL PRIMARY KEY,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
INSERT INTO MonthEndings VALUES(201101, '27/12/2010', '26/01/2011');
INSERT INTO MonthEndings VALUES(201102, '27/01/2011', '26/02/2011');
INSERT INTO MonthEndings VALUES(201103, '27/02/2011', '26/03/2011');
INSERT INTO MonthEndings VALUES(201112, '27/11/2011', '26/01/2012');
You can then group accurately using:
SELECT M.MonthID, P.Id, P.Date, P.Amount
FROM Payments AS P
JOIN MonthEndings AS M ON P.Date BETWEEN M.StartDate and M.EndDate
ORDER BY M.MonthID, P.Date;
Any group headings etc are best handled out of the DBMS - the SQL gets you the data in the correct sequence, and the software retrieving the data presents it to the user.
If you can't translate SQL to LINQ, that makes two of us. Sorry, I have never used LINQ, so I've no idea what is involved.
SELECT *, CASE WHEN datepart(day,date)<27 THEN datepart(month,date)
ELSE datepart(month,date) % 12 + 1 END as group_name
FROM payment

Resources