sum of last month and total - sql

Im trying to get sum of points for a user in the last month and the total, is it possible to get it in one query? Im using zend but i can probably get it working with provided sql.
heres my total for last month
$select = $this->_db
->select()
->from(array('p' => $this->_name), array(
'user_login',
'sum' => new Zend_Db_Expr('SUM(p.value)'),
)
)
->joinLeft(array('u' => 'user'), 'p.user_login = u.login')
->group('p.user_login')
->where('DATE(when) >= CURDATE() - INTERVAL 30 DAY')
->order('sum DESC')
;
return $this->getAdapter()->fetchAll($select);

As I understand the question, you want to show the total for the last 30 days and the total of all times for each user; sadly I can't test the Zend syntax and I'm not very used to it, but here is one version of the required SQL for MySQL;
SELECT p.user_login,
SUM(IF(DATE(`when`) >= CURDATE() - INTERVAL 30 DAY,p.value,0)) sum,
SUM(p.value) total_sum
FROM user_value p
JOIN user u
ON u.login = p.user_login
GROUP BY p.user_login
ORDER BY sum DESC;
SQLfiddle for testing.

I believe your initial query is equivalent to this SQL:
SELECT p.user_login, SUM(p.value) as sum_last_month
FROM person p
LEFT JOIN user u ON p.user_login = u.login
WHERE DATE(when) >= CURDATE() - INTERVAL 30 DAY
GROUP BY p.user_login
ORDER BY sum_last_month DESC
I'm not sure how to do a nested SELECT like this with Zend, but in SQL I believe something like this would work:
SELECT p.user_login, SUM(p.value) as sum_last_month,
(SELECT SUM(p2.value) FROM person p2 WHERE p2.id = p.id) as total
FROM person p
LEFT JOIN user u ON p.user_login = u.login
WHERE DATE(when) >= CURDATE() - INTERVAL 30 DAY
GROUP BY p.user_login
ORDER BY sum_last_month DESC, total DESC

Related

Calculate rolling year totals in sql

I am gathering something that is essentially am "enrollment date" for users. The "enrollment date" is not stored in the database (for a reason too long to explain here), so I have to deduce it from the data. I then want to reuse this CTE in numerous places throughout another query to gather values such as "total orders 1 year before enrollment" and "total orders 1 year after enrollment".
I haven't gotten this code to run, as it's much more complex in my actual data set (this code is paraphrased from the actual code) and I have a feeling it's not the best way to do this. As you can see, my date conditionals are mostly just placeholders, but I think it should be obvious what I am trying to do.
That said, I think this would mostly work. My question is, is there a better way to do this? Additionally, could I combine the rolling year before and rolling year after into one table somehow? (maybe window functions)? This is part of a much bigger query, so the more consolidation I could do, the better it would seem.
For what it's worth, the subquery to derive the "enrollment date" is also more complex than shown here.
With enroll as (Select
user_id,
MIN(date) as e_date
FROM `orders` o
WHERE (subscribed = True)
group by user_id
)
Select*
from users
left join (select
user_id,
SUM(total_paid)
from orders where date > (select enroll.e_date where user_id = user_id) AND date < (select enroll.e_date where user_id = user_id + 365 days)
and order_type = 'special'
group by user_id
) as rolling_year_after on rolling_year_after.user_id = users.user_id
left join (select
user_id,
SUM(total_paid)
from orders where date < (select enroll.e_date where user_id = user_id) and date > (select enroll.e_date where user_id = user_id - 365 days)
and order_type = 'special'
group by user_id
) as rolling_year_before on rolling_year_before.user_id = users.user_id
Maybe something like this, not sure if its more performant, but looks a bit cleaner:
With enroll as (Select
user_id,
MIN(date) as e_date
FROM `orders` o
WHERE (subscribed = True)
group by user_id
)
, rolling_year as (
select
user_id,
SUM(CASE WHEN date between enroll.edate and enroll.edate + 365 days then (total_paid) else 0 end) as rolling_year_after,
SUM(CASE WHEN date between enroll.edate - 365 days and enroll.edate then (total_paid) else 0 end) as rolling_year_before
from orders
left join enroll
on order.user_id = enroll.user_id
where order_type = 'special'
group by user_id
)
Select *
from users
left join rolling_year
on users.user_id = rolling_year.user_id

oracle query for get 20 top agency which have pass issued

I have a query which show datewise pass issued by agency. I wanted to get top 20 agency who have most pass issued here is my query
Nothing in your data id identifies "agency". If I assume you mean "agent", you can get the top 20 by aggregating and then limiting the result. In Oracle 12C+, you can use:
SELECT gp.agent_id, a.agent_name, COUNT(*)
FROM eofficeuat.gatepass gp INNER JOIN
eofficeuat.cnf_agents a
ON gp.agent_id = a.agent_id INNER JOIN
eofficeuat.cardprintlog_user u
ON gp.agent_id = u.agent_id
WHERE gp.issuedatetime BETWEN DATE '2019-09-28' AND DATE '2019-09-29'
GROUP BY gp.agent_id, a.agent_name
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;
In earlier versions, a subquery is needed:
SELECT *
FROM (SELECT gp.agent_id, a.agent_name, COUNT(*)
FROM eofficeuat.gatepass gp INNER JOIN
eofficeuat.cnf_agents a
ON gp.agent_id = a.agent_id INNER JOIN
eofficeuat.cardprintlog_user u
ON gp.agent_id = u.agent_id
WHERE gp.issuedatetime BETWEEN DATE '2019-09-28' AND DATE '2019-09-29'
GROUP BY gp.agent_id, a.agent_name
ORDER BY COUNT(*) DESC
) a
WHERE rownum <= 20;
Obviously, if you do mean "agency" and that is identified by different columns, you would just adjust the SELECT and GROUP BY clauses.
Also, I would advise you never to use BETWEEN on dates in Oracle. There is a time component that might cause issues.
If you intend only times on '2019-09-28', then:
gp.issuedatetime >= DATE '2019-09-28' AND
gp.issuedatetime < DATE '2019-09-29'
If you intend both the 28th and 29th:
gp.issuedatetime >= DATE '2019-09-28' AND
gp.issuedatetime < DATE '2019-09-30'
You can use LIMIT clause(12c or higher version) with TOP 20 records as following:
SELECT eofficeuat.gatepass.agent_id, eofficeuat.cnf_agents.agent_name, COUNT(1) as cnt
FROM eofficeuat.gatepass INNER JOIN
eofficeuat.cnf_agents
ON eofficeuat.gatepass.agent_id = eofficeuat.cnf_agents.agent_id INNER JOIN
eofficeuat.cardprintlog_user
ON eofficeuat.gatepass.agent_id = eofficeuat.cardprintlog_user.agent_id
WHERE eofficeuat.gatepass.issuedatetime BETWEN DATE '2019-09-28' AND DATE '2019-09-29'
GROUP BY eofficeuat.gatepass.agent_id, eofficeuat.cnf_agents.agent_name
ORDER BY cnt DESC
FETCH FIRST 20 ROWS ONLY; -- this will fetch top 20 agents
Cheers!!

A query that select email based in the user time

I'm trying to create a system that will select the old users that have not logged in for the past 7 days. I have a problem with this query.
The query should select the a.email, p.name, a.name, b.account_id, and I'll explain.
a is accounts
b is billing
p is players
Should check if the b.account_id is equal a.id that can get by p.account_id and after that should check if the p.lastlogin is higher or equal than 7 days then should return the query results.
I tried this, but it isn't working:
SELECT
`p`.`name`,
`a`.`email`,
`a`.`name`,
`b`.`account_id`
FROM
`billing` AS `b` AND `players` AS `p`
LEFT JOIN `accounts` AS `a`
ON `a`.`id` = `p`.`account_id` AND `a`.`name` = `b`.`account_id`
WHERE `p`.`lastlogin` >= UNIX_TIMESTAMP() + (7 * 86400)
AND group_id = 1
ORDER BY lastlogin
DESC
I hope that this is understandable, xD.
Regards,
vankk.
I think the problem is here.
WHERE `p`.`lastlogin` >= UNIX_TIMESTAMP() + (7 * 86400)
It should be -instead of +.
Also, why don't you use 2 JOINs?
You can also use the DATEDIFF function if you are using SQL
where DATEDIFF(d, a.DateValue , DateTimeNow) <7;
https://www.w3schools.com/sql/func_datediff.asp

Trying to create a SQL query

I am trying to create a query that retrieves only the ten companies with the highest number of pickups over the six-month period, this means pickup occasions, and not the number of items picked up.
I have done this
SELECT *
FROM customer
JOIN (SELECT manifest.pickup_customer_ref reference,
DENSE_RANK() OVER (PARTITION BY manifest.pickup_customer_ref ORDER BY COUNT(manifest.trip_id) DESC) rnk
FROM manifest
INNER JOIN trip ON manifest.trip_id = trip.trip_id
WHERE trip.departure_date > TRUNC(SYSDATE) - interval '6' month
GROUP BY manifest.pickup_customer_ref) cm ON customer.reference = cm.reference
WHERE cm.rnk < 11;
this uses dense_rank to determine the order or customers with the highest number of trips first
Hmm well i don't have Oracle so I can't test it 100%, but I believe your looking for something like the following:
Keep in mind that when you use group by, you have to narrow down to the same fields you group by in the select. Hope this helps at least give you an idea of what to look at.
select TOP 10
c.company_name,
m.pickup_customer_ref,
count(*) as 'count'
from customer c
inner join mainfest m on m.pickup_customer_ref = c.reference
inner join trip t on t.trip_id = m.trip_id
where t.departure_date < DATEADD(month, -6, GETDATE())
group by c.company_name, m.pickup_customer_ref
order by 'count', c.company_name, m.pickup_customer_ref desc

Having clause being ignored

In this query, I am attempting to get a count that gives me a count of patients for each practice under given conditions.
The issue is that I have to show patients who have had >=3 office visits in the past year.
Count(D.PID)
in the select list is ignoring
HAVING count(admitdatetime)>=3
Here is my query
select distinct D.PracticeAbbrevName, D.ProviderLastName, count(D.pid) AS Count
from PersonDetail AS D
left join Visit AS V on D.PID = V.PID
where D.A1C >=7.5 and V.admitdatetime >= (getdate()-365) and D.A1CDays <180 and D.Diabetes = 1
group by D.PracticeAbbrevName, D.ProviderLastName
having count(admitdatetime)>=3
order by PracticeAbbrevName
If I get rid of the count function for D.pid, and just display each PID individually, my having phrase works properly.
There is something about count and having that do now work properly together.
Revised answer:
SELECT DISTINCT
D.PracticeAbbrevName,
D.ProviderLastName,
COUNT(D.pid) AS PIDCount,
COUNT(admitdatetime) AS AdmitCount
FROM
PersonDetail AS D
LEFT JOIN Visit AS V
ON D.PID = V.PID
WHERE
D.A1C >= 7.5
AND V.admitdatetime >= ( GETDATE() - 365 )
AND D.A1CDays < 180
AND D.Diabetes = 1
GROUP BY
D.PracticeAbbrevName,
D.ProviderLastName
HAVING
COUNT(admitdatetime) >= 3
ORDER BY
PracticeAbbrevName
You're trying to do too much at once. Split the logic in 2 steps:
Query grouping by PID to filter out patients that don't meet your criteria.
Query grouping by practice to get a patient count.
Your query would look like this:
;with EligiblePatients as (
select d.pid,
d.PracticeAbbrevName,
d.ProviderLastName
from PersonDetail d
left join Visit v
on v.pid = d.pid
and v.admitdatetime >= (getdate()-365)
where d.A1C >= 7.5
and d.A1CDays < 180
and d.Diabetes = 1
group by d.pid,
d.PracticeAbbrevName,
d.ProviderLastName
having count(v.pid) >= 3
)
select PracticeAbbrevName,
ProviderLastName,
COUNT(*) as PatientCount
from EligiblePatients
group by PracticeAbbrevName,
ProviderLastName
order by PracticeAbbrevName