How can I sum columns using conditions? - mysql

I'm trying to do the sum when:
date_ini >= initial_date AND final_date <= date_expired
And if it is not in the range will show the last net_insurance from insurances
show last_insurance when is not in range
Here my tables:
POLICIES
ID POLICY_NUM DATE_INI DATE_EXPIRED TYPE_MONEY
1, 1234, "2013-01-01", "2014-01-01" , 1
2, 5678, "2013-02-01", "2014-02-01" , 1
3, 9123, "2013-03-01", "2014-03-01" , 1
4, 4567, "2013-04-01", "2014-04-01" , 1
5, 8912, "2013-05-01", "2014-05-01" , 2
6, 3456, "2013-06-01", "2014-06-01" , 2
7, 7891, "2013-07-01", "2014-07-01" , 2
8, 2345, "2013-08-01", "2014-08-01" , 2
INSURANCES
ID POLICY_ID INITIAL_DATE FINAL_DATE NET_INSURANCE
1, 1, "2013-01-01", "2014-01-01", 100
2, 1, "2013-01-01", "2014-01-01", 200
3, 1, "2013-01-01", "2014-01-01", 400
4, 2, "2011-01-01", "2012-01-01", 500
5, 2, "2013-01-01", "2014-01-01", 600
6, 3, "2013-01-01", "2014-01-01", 100
7, 4, "2013-01-01", "2014-01-01", 200
I should have
POLICY_NUM NET
1234 700
5678 600
9123 100
4567 200
Here is what i tried
SELECT p.policy_num AS policy_num,
CASE WHEN p.date_ini >= i.initial_date
THEN SUM(i.net_insurance)
ELSE (SELECT max(id) FROM insurances GROUP BY policy_id) END as net
FROM policies p
INNER JOIN insurances i ON p.id = i.policy_id AND p.date_ini >= i.initial_date
GROUP BY p.id
Here is my query http://sqlfiddle.com/#!2/f6077b/16
Somebody can help me with this please?
Is not working when is not in the range it would show my last_insurance instead of sum

Try it this way
SELECT p.policy_num, SUM(i.net_insurance) net_insurance
FROM policies p JOIN insurances i
ON p.id = i.policy_id
AND p.date_ini >= i.initial_date
AND p.date_expired <= i.final_date
GROUP BY i.policy_id, p.policy_num
UNION ALL
SELECT p.policy_num, i.net_insurance
FROM
(
SELECT MAX(i.id) id
FROM policies p JOIN insurances i
ON p.id = i.policy_id
AND (p.date_ini < i.initial_date
OR p.date_expired > i.final_date)
GROUP BY i.policy_id
) q JOIN insurances i
ON q.id = i.id JOIN policies p
ON i.policy_id = p.id
Output:
| POLICY_NUM | NET_INSURANCE |
|------------|---------------|
| 1234 | 700 |
| 5678 | 600 |
| 9123 | 100 |
| 4567 | 200 |
Here is SQLFiddle demo

This will ensure that you only get the last insurance if the policy has no insurances within the policy period.
SELECT policy_num, SUM(IF(p.cnt > 0, p.net_insurance, i.net_insurance)) AS net_insurance
FROM
(
SELECT
p.id,
p.policy_num,
SUM(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
i.net_insurance, 0)) AS net_insurance,
SUM(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
1, 0)) AS cnt,
MAX(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
0, i.id)) AS max_i_id
FROM policies p
INNER JOIN insurances i ON p.id = i.policy_id
GROUP BY p.id
) as p
LEFT JOIN insurances i ON i.id = p.max_i_id
GROUP BY p.id
Here is my SQLFiddle

Try splitting grouping from conditioning :)
SELECT
policy_num
, CASE
WHEN TRUE
AND p.date_ini <= src.initial_date
AND p.date_expired >= src.final_date
THEN src.sum_insurances
ELSE i.net_insurance
END AS something
FROM (
SELECT
max(id) AS latest_id
, sum(net_insurance) AS sum_insurances
, initial_date
, final_date
, policy_id
FROM insurances
GROUP BY policy_id
) src
JOIN policies p ON p.id = src.policy_id
JOIN insurances i ON i.id = src.latest_id
ORDER BY p.id

Related

loop over a date list (or any list) and append queries in mysql or snowflake

I am new to sql language and recently snowflake. I have a table that contains all checkin dates for all users for a business
user_id | checkin_date
001 03-06-2018
001 07-07-2018
001 08-01-2018
002 03-19-2018
002 03-27-2018
002 07-11-2018
Now I want to do a query such that I can look back from a query_date to see how many times each user checked in between query_date - 7 and query_date, qyery_date - 90 and query date ... the following snowflake query does the job properly for query_date='2018-08-01'.
with user_checkin_history_sum as (
select
user_id,
sum(iff(datediff(DAY, uc.checkin_date, '2018-08-01') <= 7, 1, 0)) as visits_past_7_days,
sum(iff(datediff(DAY, uc.checkin_date, '2018-08-01') <= 90, 1, 0)) as visits_past_90_days,
from user_checkin as uc
where uc.checkin_date < '2018-08-01'
group by user_id
order by user_id
)
This gives me result
user_id | visits_past_7_days | visits_past_90_days
001 0 2
002 0 1
My question is, if I have more than one day as the query_date, i.e., I have a list of checkin_date, for each checkin_date in the list, I do the query as above and append all them together. Basically, it is a loop over + table append, but I do not find an answer how to do this in sql language. Essentially, what I want to do is like the following
with user_checkin_history_sum as (
select
user_id,
sum(iff(datediff(DAY, uc.checkin_date, query_date) <= 7, 1, 0)) as visits_past_7_days,
sum(iff(datediff(DAY, uc.checkin_date, query_date) <= 90, 1, 0)) as visits_past_90_days,
from user_checkin as uc
where uc.checkin_date < query_date and
LOOP OVER
query_date in ('2018-08-01', '2018-06-01')
group by user_id
order by user_id
)
And hopefully it gives this result
user_id | query_date | visits_past_7_days | visits_past_90_days
001 '08-01-2018' 0 2
002 '08-01-2018' 0 1
001 '06-01-2018' 0 1
002 '06-01-2018' 0 2
You should be able to cross join a table containing all the dates you want to examine:
WITH dates AS (
SELECT '2018-06-01' AS query_date UNION ALL
SELECT '2018-08-01' UNION ALL
... -- maybe other dates as well
),
user_checkin_history_sum AS (
SELECT
uc.user_id,
d.query_date,
SUM(IFF(DATEDIFF(DAY, uc.checkin_date, d.query_date) <= 7, 1, 0)) AS visits_past_7_days,
SUM(IFF(DATEDIFF(DAY, uc.checkin_date, d.query_date) <= 90, 1, 0)) AS visits_past_90_days
FROM dates d
CROSS JOIN user_checkin AS uc
WHERE uc.checkin_date < '2018-08-01'
GROUP BY d.query_date, uc.user_id
ORDER BY d.query_date, uc.user_id
)

MYSQL join not selecting correct row

I have two tables
1) outreach
id profile_id url
-------------------------
1 2 www.test.com
2 3 www.google.com
3 4 www.example.com
2). outreach_links
id outreach_id end_date status
------------------------------------
1 1 2016-12-28 00:00:00 Approved
2 1 2016-12-16 00:00:00 Approved
3 1 NUll Pending
4 1 2016-12-11 00:00:00 Approved
I have this SQL Query with Left Join and Conditions that is working fine except I want to select the whole ROW of the MAX end_date that meets the 3 condition. so in this case the first row with end_date = 2016-12-28 00:00:00
select o.*,ol.*,MAX(ol.end_date) as max_date, SUM(ol.status = "Approved" and (ol.end_date > Now() or end_date is null)) as cond1, SUM(ol.status = "Pending") as cond2,
SUM(ol.status = "Approved" and (ol.end_date < Now() and ol.end_date is not null)) as cond3
FROM outreach o
LEFT JOIN outreach_links ol on ol.outreach_id = o.id
WHERE o.profile_id=2
GROUP BY o.id
HAVING (cond1 = 0 and cond2 = 0) or (cond1 = 0 and (cond2 = 1 and cond3 >=1))
ORDER BY ol.end_date desc
but this is the output for this query ( its picking the pending for some reason) >>
+"id": "3"
+"profile_id": "2"
+"url": "www.test.com"
+"outreach_id": "1"
+"end_date": null
+"status": "Pending"
+"max_date": "2016-12-28 00:00:00"
+"cond1": "0"
+"cond2": "1"
+"cond3": "3"
I want to get this instead
+"id": "1"
+"profile_id": "2"
+"url": "www.test.com"
+"outreach_id": "1"
+"end_date": 2016-12-28 00:00:00
+"status": "Approved"
+"max_date": "2016-12-28 00:00:00"
+"cond1": "0"
+"cond2": "1"
+"cond3": "3"
The first row with MAX end date, how can I do that keeping this same Query ??
Thanks
See SQL Select only rows with Max Value on a Column for how to get the row with the max end_date for each outreach_id. Then join with that row to get the latest status.
SELECT o.*, ol1.max_date, ol2.status, SUM(ol.status = "Approved" and (ol.end_date > Now() or end_date is null)) as cond1, SUM(ol.status = "Pending") as cond2,
SUM(ol.status = "Approved" and (ol.end_date < Now() and ol.end_date is not null)) as cond3
FROM outreach o
LEFT JOIN outreach_links AS ol ON ol.outreach_id = o.id
LEFT JOIN (SELECT outreach_id, MAX(end_date) AS max_date
FROM outreach_links
GROUP BY outreach_id) AS ol1 ON ol1.outreach_id = o.id
LEFT JOIN outreach_links ol2 on ol2.outreach_id = o.id AND ol2.end_date = ol1.max_date
WHERE o.profile_id=2
GROUP BY o.id
HAVING (cond1 = 0 and cond2 = 0) or (cond1 = 0 and (cond2 = 1 and cond3 >=1))
ORDER BY ol.end_date desc

MYSQL Query Select with condition & Join

I have these 3 tables ( with these structure):
outreach
id url profile_id
------------------------------------------
40 www.google.com 2
41 www.yahoo.com 3
42 www.test.com 1
outreach_links
id outreach_id end_date status
-----------------------------------------------
1 41 2016-01-12 Pending
2 40 2016-03-12 Pending
3 40 2016-02-12 Approved
comments
id outreach_id name
----------------------------
1 40
2 40
3 40
and I have this Query:
select o.*,
SUM(if(ol.status = "Approved" and (ol.end_date > now() or end_date is null), 1, 0)) as cond1,
SUM(if(ol.status = "Pending" and (ol.end_date != now() or end_date is null), 1, 0)) as cond2,
SUM(if(ol.status = "Pending" and (ol.end_date < now()), 1, 0)) as cond3
from outreach o
left join outreach_links ol on ol.outreach_id = o.id
where o.profile_id=2
group by o.id
having (cond1 = 0 and cond2 = 0) or (cond1 = 0 and (cond2 = 1 and cond3 >=1)) order by ol.end_date desc
I am trying to fix this Query and make it also select the following:
1). ol.* ONLY if MAX(end_date) and
2). Count(id.comment) count all comments for that particular row
is that possible?
right now here is the output
+"id": "40"
+"profile_id": "2"
+"url": "http://www.google.com"
+"created_at": "2016-12-05 21:55:10"
+"updated_at": "2016-12-05 22:49:56"
+"cond1": "0"
+"cond2": "0"
+"cond3": "5"
I want to add
+"max_date": get me max of end_date and the whole row of the row highlighted
+"Count(comments)": get me all the comments count for this one which is 3
Thanks
Are you trying to get the latest update date? The following query should give you the latest updated date.
However, I do not understand what you are trying to get for cond1, cond2, cond3, and what should be populated as created_date, and updated_date? Can you please give definitions for these fields?
SELECT o.*, ol.*, COUNT(c.id)
FROM outreach o
LEFT JOIN outreach_links ol ON ol.outreach_id = o.id
LEFT JOIN comments c ON c.outreach_id = o.id
WHERE ol.id = (SELECT ol2.id
FROM outreach_links ol2
WHERE ol2.outreach_id = ol.outreach_id
ORDER BY ol2.end_date, ol2.id DESC
LIMIT 1)
OR ol.id IS NULL
GROUP BY o.id, ol.id

Why MySQL full outer join returns nulls?

Why MySQL full outer join returns nulls?
Hi
I have the following data:
s_id,date,p_id,amount_sold
1, '2015-10-01', 1, 10
2, '2015-10-01', 2, 12
7, '2015-10-01', 1, 11
3, '2015-10-02', 1, 11
4, '2015-10-02', 2, 10
5, '2015-10-15', 1, 22
6, '2015-10-16', 2, 20
8, '2015-10-22', 3, 444
and i want my query to output something like this: (A = sum of amount_sold for p_id=1 for that date,B = sum of amount_sold for p_id=2 for that date)
date,A,B,Difference
'2015-10-01',21,12,9
'2015-10-02',11,10,1
'2015-10-15',22,0,22
'2015-10-01',0,20,-20
I tried with this query, but the order its returning is having NULLS and the output is wrong:
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
LEFT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where A.p_id=1 or B.p_id=2
group by A.date, A.p_id
UNION
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
RIGHT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where B.p_id=2
group by A.date, A.p_id
It returned:
p_id date A Bs difference
1 2015-10-01 21 NULL NULL
2 2015-10-01 12 12 0
1 2015-10-02 11 NULL NULL
2 2015-10-02 10 10 0
1 2015-10-15 22 NULL NULL
2 2015-10-16 20 20 0
What am i doing wrong here? and what is the correct way of doing it? any help would be appreciated.
A full join isn't needed. You can use conditional aggregation instead:
select
date,
sum(case when p_id = 1 then amount_sold else 0 end) a,
sum(case when p_id = 2 then amount_sold else 0 end) b,
sum(case when p_id = 1 then amount_sold else 0 end)
- sum(case when p_id = 2 then amount_sold else 0 end) difference
from sales
where p_id in (1,2)
group by date

How to find a list of rows where start & end almost matches a time?

I have a table containing some time presences, like this :
presences (id, account_id, start, end, date);
And here's some entries :
1, 1, 10:00:00, 11:00:00, 2013-07-02
2, 2, 10:05:00, 11:05:00, 2013-07-02
3, 3, 9:55:00, 11:10:00, 2013-07-02
4, 4, 10:02:00, 10:58:00, 2013-07-02
5, 1, 14:00:00, 15:30:00, 2013-07-02
6, 2, 14:03:00, 15:36:00, 2013-07-02
7, 3, 13:56:00, 15:28:00, 2013-07-02
8, 4, 14:05:00, 15:30:00, 2013-07-02
As you can see, the accounts have a start and end time around 10-11/14-15h, but it's not always exactly the same, so I cannot search WHERE start = 14:00:00 AND end = 15:30:00 for example.
For the query, I have the date and the values of a single row (id: 1), and I like MySQL to return me all the presences that have the same date, is not {id} and have start and end in that same range of time
So far, I have this implementation, but I'm looking for a better way (if it exists) to match the results :
I get the middle time of start + (end - start / 2) (for id:1, this means 10:30:00), and I search like this :
SELECT *
FROM presences
WHERE id != 1
AND start < 10:30:00 AND end > 10:30:00
AND date = 2013-07-02
id and date are the values found from the query (SELECT * FROM presences WHERE id = 1), and start and end are computed as explained before.
Is there a better way to found similar times ?
You can do this in one query:
SELECT *
FROM presences p cross join
(select p.* from presences p where id = 1
) p1
where p.id <> p1.id and
p.date = p1.date and
p.start < p1.start + (p1.end - p1.start / 2) and
p.end > p1.start + (p1.end - p1.start / 2)
If you want any overlapping, then use this logic:
SELECT *
FROM presences p cross join
(select p.* from presences p where id = 1
) p1
where p.id <> p1.id and
p.date = p1.date and
(p.start < p1.end and
p.end > p1.start
)
If you want start and end in the range, then look for that explicitly:
SELECT *
FROM presences p cross join
(select p.* from presences p where id = 1
) p1
where p.id <> p1.id and
p.date = p1.date and
p.start between p1.start + p1.end and
p.end between p1.start + p1.end
You could add/subtract a few minutes from p1.start and p1.end to make the match fuzzier.