Doctrine Query Giving Unexpected Results Within ZF2 Service - doctrine-orm

I am receiving unexpected results from the Doctrine query below. I am unsure of the reason why.
This is the query I am trying to create:
SELECT `subscription_reference`
FROM `member_subscriptions`
WHERE `list` = 1 AND `quantity` = 1
AND `payment_received` = 1 AND `email` = 'user#domain.com'
AND `subscription_consent_date` != '0000-00-00 00:00:00'
AND ( ( `subscription_expires` = '0000-00-00' )
OR ( '2015-01-22' <= `subscription_expires` ) )
These are the commands I used I assemble the query:
$query1 = $this->entityManager
->createQueryBuilder()
->select('s.subscriptionReference')
->from('AMDatabase\Entity\Membership\MemberSubscriptions', 's');
$query1->andWhere($query1->expr()->eq('s.list', ':list'))
->setParameter('list', 1);
$query1->andWhere($query1->expr()->eq('s.quantity', ':quantity'))
->setParameter('quantity', 1);
$query1->andWhere($query1->expr()->eq('s.paymentReceived', ':paymentReceived'))
->setParameter('paymentReceived', 1);
$query1->andWhere($query1->expr()->eq('s.email', ':email'))
->setParameter('email', $params['email_address']);
$query1->andWhere($query1->expr()->notLike('s.subscriptionConsentDate', ':subscriptionConsentDate'))
->setParameter('subscriptionConsentDate', new \DateTime('0000-00-00 00:00:00'));
$query1->orWhere($query1->expr()->eq('s.subscriptionExpires', ':subscriptionExpires1'))
->setParameter('subscriptionExpires1', '0000-00-00');
$query1->orWhere($query1->expr()->lt('s.subscriptionExpires', ':subscriptionExpires2'))
->setParameter('subscriptionExpires2', gmdate('Y-m-d'));
$result1 = $query1->getQuery()->getArrayResult();
if (is_array($result1) && count($result1) > '0') {
foreach ($result1 AS $value1) {
print_r( $value1 );
}
}
This is an example result the Doctrine query produces:
subscription_reference 38
member_reference 6
list 1
quantity 1
payment_received 1
email user#domain.com
subscription_consent_reference 0
subscription_consent_date 0000-00-00 00:00:00
subscription_consent_ip_address
subscription_consent_granted_name
subscription_begins 2005-11-05
subscription_expires 2014-06-30
email_delivery_hour 0
email_delivery_timezone_reference 241
last_mailing 2014-06-30
administrator_update_reference 0
administrator_update_timestamp 0000-00-00 00:00:00
The unexpected results are:
subscription_consent_date having a value of '0000-00-00 00:00:00'
I need it to have an actual date, such as '2005-11-05 11:30:34'
subscription_expires having a value of '2014-06-30'
I need this date to be either in the future or '0000-00-00' (meaning the subscription is ongoing)
multiple subscription_reference
Multiple subscriptions are being selected, instead of the one I have tried to describe. I was expecting the e-mail address given in the query to isolate from all other subscribers.
Would someone show me how to correct the mistakes I have made? Thank you.

The 2 possibilities for s.subscriptionExpires need to be within the same "andWhere"
$query1 = $this->entityManager
->createQueryBuilder()
->select('s.subscriptionReference')
->from(
'AMDatabase\Entity\Membership\MemberSubscriptions',
's'
);
$query1
->andWhere(
$query1->expr()
->eq(
's.list',
':list'
)
)
->setParameter(
'list',
1
);
$query1
->andWhere(
$query1->expr()
->eq(
's.quantity',
':quantity'
)
)
->setParameter(
'quantity',
1
);
$query1
->andWhere(
$query1->expr()
->eq(
's.paymentReceived',
':paymentReceived'
)
)
->setParameter(
'paymentReceived',
1
);
$query1
->andWhere(
$query1->expr()
->eq(
's.email',
':email'
)
)
->setParameter(
'email',
$params['email_address']
);
$query1
->andWhere(
$query1->expr()
->notLike(
's.subscriptionConsentDate',
':subscriptionConsentDate'
)
)
->setParameter(
'subscriptionConsentDate',
new \DateTime('0000-00-00 00:00:00')
);
$query1
->andWhere('s.subscriptionExpires = :subscriptionExpires1 OR s.subscriptionExpires >= :subscriptionExpires2')
->setParameter(
'subscriptionExpires1',
'0000-00-00'
)
->setParameter(
'subscriptionExpires2',
gmdate('Y-m-d')
);
$result1 = $query1->getQuery()
->getArrayResult();
I was given the following code to see the query being created to trouble shoot what was wrong:
$q = $query1->getQuery();
$sql = $q->getSQL();
print_r( $sql );

Related

Powerbi to return a 'NA' using the IF that is used in a std table

I have a code in a powerbi table that displays all. It basically counts how long a particular task is performed for mgt. It was done by someone else. I am trying to find out how to get the rows to display a 'No Data' or 'NA' if there is no data. I tried several approaches but could not get it to work. Any suggestions? Linda from Wisconsin.
TimeTaken =
//----CALCULATIONS
VAR DIFF = 'Opened'[Resolved] - 'Opened'[Opened]
VAR NumOfMinutes = DIFF * 24
* 60
VAR DAYS =
IF ( DIFF >= 1, INT ( DIFF ), BLANK () )
VAR HOURS =
INT ( ( DIFF - DAYS ) * 24 )
VAR MINUTES = NumOfMinutes
- ( DAYS * 24
* 60 )
- ( HOURS * 60 )
//---TEXTS
VAR DaysText =
IF ( DAYS >= 1, FORMAT ( DAYS, "00" ) & "days" )
//ELSE ("No Data") >>>>> this not working.. I deleted some
VAR HoursText =
FORMAT ( HOURS, "00" ) & "hrs"
VAR MinutesText =
FORMAT ( MINUTES, "00" ) & "mins"
RETURN
COMBINEVALUES ( " ", DaysText, HoursText, MinutesText )
Hi You can try below code. Just replace your last two line with this code.
RETURN
IF (
ISBLANK ( DaysText ) && ISBLANK ( HoursText )
&& ISBLANK ( MinutesText ),
"NA",
COMBINEVALUES ( " ", DaysText, HoursText, MinutesText )
)

Calculating total presence time from overlapping login intervals

I would like to create a DAX measure in Power BI to calculate the total man-hours the users were logged into a system. It is possible that users were logged in on multiple workstations at the same time, so the logged-in intervals may overlap.
I have the following test data:
I have come up with a measure that works in most cases (Name: A, B, E).
Man Hours = SUMX(
SUMMARIZE(Logins, Logins[Name], "x",
var l = Logins
return
CONVERT(
SUMX(
FILTER(Logins,
var t = [End]
return COUNTROWS(FILTER(l, [Start] <= t && t <= [End])) == 1
),
[End])
- SUMX(
FILTER(Logins,
var t = [Start]
return COUNTROWS(FILTER(l, [Start] <= t && t <= [End])) == 1
), [Start]
)
, DOUBLE
)
), [x]
) * 24
It fails when any of the start or end of the users intervals match exactly (e.g. all the sessions of the user were terminated by the system automatic with the same timestamp) (Name: C,D):
In the lower right part of the screen are the expected values of the man-hour subtotals.
How can I fix my measure so it can handle these edge cases?
I have sidestepped the issue by adding 1 second to the start or end of the intervals where the timestamp was matching.
Fudge_start =
VAR same_start =
FILTER (
ALL ( Logins ),
[Name] = EARLIER ( Logins[Name] )
&& [Start] = EARLIER ( [Start] )
)
VAR max_end =
MAXX ( same_start, [End] )
VAR min_id =
MINX ( FILTER ( same_start, [End] = max_end ), [Id] )
RETURN
IF (
COUNTROWS ( same_start ) > 1
&& [End] = max_end
&& [Id] = min_id,
[Start] - TIME ( 0, 0, 1 ),
[Start]
)
and
Fudge_end =
VAR same_end =
FILTER (
ALL ( Logins ),
[Name] = EARLIER ( Logins[Name] )
&& [End] = EARLIER ( [End] )
)
VAR max_start =
MAXX ( same_end, [Start] )
VAR min_id =
MINX ( FILTER ( same_end, [Start] = max_start ), [Id] )
RETURN
IF (
COUNTROWS ( same_end ) > 1
&& [Start] = max_start
&& [Id] = min_id,
[End] + TIME ( 0, 0, 1 ),
[End]
)
I have also updated the measure accordingly:
Man Hours =
SUMX(
SUMMARIZE(Logins, Logins[Name], "x",
var l = Logins
return CONVERT(
SUMX(
FILTER(Logins,
var t = [Fudge_end]
var i = [Id]
return COUNTROWS(FILTER(Logins, [Fudge_start] <= t && t <= [Fudge_end] )) == 1
), [End]
) -
SUMX(
FILTER(Logins,
var t = [Fudge_start]
var i = [Id]
return COUNTROWS(FILTER(Logins, [Fudge_start] <= t && t <= [Fudge_end] )) == 1
), [Start]
), DOUBLE
)
), [x]
) * 24
Result:

If it is the first date or last date use one aggregation; if any other day, use another aggregation

Let's assume I have the below dataset.
What I need to create the below matrix where if it is the beginning or month end, I aggregate A or B in Category 1 and calculate SUM but if it is any other day in a month but 1st or last, I am tagging A or B in Category 2 and calculate SUM. I guess I need to use SWITCH, don't I?
Edit in info from comments
Like to create 3 col:
isStart = IF ( main_table[date] = STARTOFMONTH ( main_table[date] ), 1, 0 )
isEnd = IF ( main_table[date] = ENDOFMONTH ( 'main_table'[date] ), 1, 0 )
in_between_date =
IF ( AND ( main_table[date] <> ENDOFMONTH ( 'main_table'[date] ),
main_table[date] <> STARTOFMONTH ( main_table[date] ) ), 1, 0 )
Then, create the columns with my categories, like
start_end =
IF ( OR ( NOT ( ISERROR ( SEARCH ( "A", main_table[code] ) ) ),
main_table[code] = "B" ),
"Category 1",
BLANK () )
and
in_between =
IF ( OR ( main_table[code] = "B", main_table[code] = "A" ), "Category 2", BLANK () )
But then, what should I use in switch/if ? = if(VALUES('main_table'[isStart]) = 1, then what?
You where on the right track but overcomplicated a bit. You only need one extra column "Category" giving for each row in what category the item falls.
Category =
IF (
startEnd[date] = STARTOFMONTH ( startEnd[date] )
|| startEnd[date] = ENDOFMONTH ( startEnd[date] );
"Category1";
"Category2"
)
table end result is:

Translating MySql query into Django ORM query

I have a query in MySql that I need translated into Django ORM. It involves joining on two tables with two counts on one of the tables. I'm pretty close to it in Django but I get duplicate results. Here's the query:
SELECT au.id,
au.username,
COALESCE(orders_ct, 0) AS orders_ct,
COALESCE(clean_ct, 0) AS clean_ct,
COALESCE(wash_ct, 0) AS wash_ct
FROM auth_user AS au
LEFT OUTER JOIN
( SELECT user_id,
Count(*) AS orders_ct
FROM `order`
GROUP BY user_id
) AS o
ON au.id = o.user_id
LEFT OUTER JOIN
( SELECT user_id,
Count(CASE WHEN service = 'clean' THEN 1
END) AS clean_ct,
Count(CASE WHEN service = 'wash' THEN 1
END) AS wash_ct
FROM job
GROUP BY user_id
) AS j
ON au.id = j.user_id
ORDER BY au.id DESC
LIMIT 100 ;
My current Django query (which brings back unwanted duplicates):
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True )
).annotate(
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = 1 )
) )
).annotate(
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = 1 )
) )
)
The above Django code produces the following query which is close but not right:
SELECT DISTINCT `auth_user`.`id`,
`auth_user`.`username`,
Count(DISTINCT `order`.`id`) AS `orders_ct`,
Count(CASE
WHEN `job`.`service` = 'clean' THEN 1
ELSE NULL
end) AS `clean_ct`,
Count(CASE
WHEN `job`.`service` = 'wash' THEN 1
ELSE NULL
end) AS `wash_ct`
FROM `auth_user`
LEFT OUTER JOIN `order`
ON ( `auth_user`.`id` = `order`.`user_id` )
LEFT OUTER JOIN `job`
ON ( `auth_user`.`id` = `job`.`user_id` )
GROUP BY `auth_user`.`id`
ORDER BY `auth_user`.`id` DESC
LIMIT 100
I could probably achieve it by doing some raw sql subqueries but I would like to remain as abstract as possible.
Based on this answer, you can write:
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True ),
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = F('job__pk') )
), distinct = True ),
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = F('job__pk') )
), distinct = True )
)
Table (after joins):
user.id order.id job.id job.service your case/when my case/when
1 1 1 wash 1 1
1 1 2 wash 1 2
1 1 3 clean NULL NULL
1 1 4 other NULL NULL
1 2 1 wash 1 1
1 2 2 wash 1 2
1 2 3 clean NULL NULL
1 2 4 other NULL NULL
Desired output for wash_ct is 2. Counting distinct values in my case/when, we will get 2.
I think this will work, the chained annotation of job might have produced duplicate users.
If not can you elaborate on duplicates you are seeing.
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True )
).annotate(
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = 1 )
) ),
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = 1 )
) )
)
Try adding values(), also when distinct=True you can combine Count()'s in one annotation().
Users.objects.values("id").annotate(
orders_ct = Count('orders', distinct = True)
).annotate(
clean_ct = Count(Case(When(job__service__exact='clean', then=1)),
distinct = True),
wash_ct = Count(Case(When(job__service__exact='wash',then=1)),
distinct = True)
).values("id", "username", "orders_ct", "clean_ct", "wash_сt")
Using values("id") should add GROUP BY 'id' for annotations and therefore prevent duplicates, see docs.
Also, there's Coalesce, but it doesn't look like it's needed, since Count() returns int anyway. And distinct, but again the distinct in Count() should be enough.
Not sure if Case needed inside Count() as it should count them anyway.

conditional group by with query api

Supose a student attandance system.
For a student and a course we have N:M relation named attandance.
Also whe have a model with attandances status (present, absent, justified, ...).
level( id, name, ... )
student ( id, name, ..., id_level )
course( id, name, ... )
status ( id, name, ...) #present, absemt, justified, ...
attandance( id, id_student, id_course, id_status, date, hour )
unique_together = ((id_student, id_course, id_status, date, hour),)
I'm looking for a list of students with >20% of absent for a level sorted by %. Something like:
present = status.objects.get( name = 'present')
justified = status.objects.get( name = 'justified')
absent = status.objects.get( name = 'absent')
#here the question. How to do this:
Student.objects.filter( level = level ).annotate(
nPresent =count( attandence where status is present or justified ),
nAbsent =count( attandence where status is absent ),
pct = nAbsent / (nAbsent + nPresent ),
).filter( pct__gte = 20 ).order_by( "-pct" )
If it is not possible to make it with query api, any workaround (lists, sets, dictionaris, ...) is wellcome!
thanks!
.
.
.
---- At this time I have a dirty raw sql writed by hand --------------------------
select
a.id_alumne,
coalesce ( count( p.id_control_assistencia ), 0 ) as p,
coalesce ( count( j.id_control_assistencia ), 0 ) as j,
coalesce ( count( f.id_control_assistencia ), 0 ) as f,
1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) ) as tpc
from
alumne a
inner join
grup g
on (g.id_grup = a.id_grup )
inner join
curs c
on (c.id_curs = g.id_curs)
inner join
nivell n
on (n.id_nivell = c.id_nivell)
inner join
control_assistencia ca
on (ca.id_estat is not null and
ca.id_alumne = a.id_alumne )
inner join
impartir i
on ( i.id_impartir = ca.id_impartir )
left outer join
control_assistencia p
on (
p.id_estat in ( select id_estat from estat_control_assistencia where codi_estat in ('P','R' ) ) and
p.id_control_assistencia = ca.id_control_assistencia )
left outer join
control_assistencia j
on (
j.id_estat = ( select id_estat from estat_control_assistencia where codi_estat = 'J' ) and
j.id_control_assistencia = ca.id_control_assistencia )
left outer join
control_assistencia f
on (
f.id_estat = ( select id_estat from estat_control_assistencia where codi_estat = 'F' ) and
f.id_control_assistencia = ca.id_control_assistencia )
where
n.id_nivell = {0} and
i.dia_impartir >= '{1}' and
i.dia_impartir <= '{2}'
group by
a.id_alumne
having
1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) )
> ( 1.0 * {3} / 100)
order by
1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) )
desc
'''.format( nivell.pk, data_inici, data_fi, tpc )
If you don't care too much whether it uses the query api or python after the fact, use itertools.groupby.
attendances = Attendance.objects.select_related().filter(student__level__exact=level)
students = []
for s, g in groupby(attendances, key=lambda a: a.student.id):
g = list(g) # g is an iterator
present = len([a for a in g if a.status == 'present'])
absent = len([a for a in g if a.status == 'absent'])
justified = len([a for a in g if a.status == 'justified'])
total = len(g)
percent = int(absent / total)
students.append(dict(name=s.name, present=present, absent=absent, percent=percent))
students = (s for s in sorted(students, key=lambda x: x['percent']) if s['percent'] > 25)
You can pass the resulting list of dicts to the view the same way you would any other queryset.