I'm stuck on this for hours, I'm trying to COUNT how many subscribers are there in Group A, Group B, Group C for this particular query:
SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(
SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber
LEFT JOIN tbl_gen_info ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) AS group_area
FROM tbl_reading_head AS rh
WHERE rh.id_soa_head IS NULL
AND rh.read_status <> 'Beginning'
AND rh.rec_status = 'active'
ORDER BY rh.id_subscriber
The sub-query gets the Group area gen_data_03 from tbl_gen_info
Tables contain this information:
tbl_gen_info
--------------------------------------------
| gen_category | gen_data_01 | gen_data_03 |
--------------------------------------------
| Area Code | Camacho St. | Group A |
--------------------------------------------
tbl_subscriber
----------------------------------
| id_subscriber | bill_area_code |
----------------------------------
| 1 | Camacho St. |
----------------------------------
tbl_reading_head
----------------------------------------------------------------------
| id_subscriber | id_soa_head | read_status | bill_month | bill_year |
----------------------------------------------------------------------
| 1 | NULL | Metered | 10 | 2017 |
----------------------------------------------------------------------
Notice that each id_subscriber has two (2) rows (one for electric, one for water). After grouping by id_subscriber:
GROUP BY rh.id_subscriber
I got this:
I tried adding COUNT before the sub-query making it:
COUNT(SELECT tbl_gen_info.gen_data_03 ...) AS group_area
but that doesn't work.
Use a subquery:
SELECT rh.group_area, COUNT(*)
FROM (SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber LEFT JOIN
tbl_gen_info
ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) as group_area
FROM tbl_reading_head rh
WHERE rh.id_soa_head IS NULL AND
rh.read_status <> 'Beginning' AND
rh.rec_status = 'active'
) rh
GROUP BY rh.group_area;
Related
Now I have that structure (very simplify):
promotion:
id | name | level1 | points1| level2 | points2 | client_id
1 | A | 10 | 12 | 20 | 15 | 1
client:
id | name | value
1 | john | 15
And that's how I calculate the level:
SELECT
name,
CASE
WHEN client.value >= promotion.level2 THEN promotion.points2
WHEN client.value >= promotion.level1 THEN promotion.points1
ELSE "None"
END as points
FROM promotion
JOIN client ON client.id = promotion.client_id
This is working well, but I would like to have such structure:
promotion:
id | name | client_id
1 | A | 1
level:
id | name | level | points | promotion_id
1 | level1 | 10 | 12 | 1
2 | level2 | 10 | 15 | 1
client:
id | name | value
1 | john | 15
But I don't have any idea how to use it in my query to get points...
SELECT
name,
CASE
???
END as points
FROM promotion
JOIN client ON client.id = promotion.client_id
LEFT JOIN level ON promotion.id= level.promotion_id
This query will give you the desired results. It uses a LEFT JOIN to find any rows in the level table which are lower than the client value, and then takes the MAX of those values, using COALESCE to set the value to None if there are no levels below the client's value:
SELECT p.name AS promotion,
c.name AS client,
COALESCE(MAX(l.points), 'None') AS points
FROM promotion p
JOIN client c ON c.id = p.client_id
LEFT JOIN level l ON l.promotion_id = p.id AND l.level < c.value
GROUP BY p.name, c.name
Output for your sample data:
promotion client points
A john 12
Demo on dbfiddle
I would use a correlated subquery:
select p.name,
(select l.points
from level l
where l.promotion_id = p.points and
l.level <= c.value
order by l.level desc
limit 1
) as points
from promotion p join
client c
on c.id = p.client_id;
The point of using a correlated subquery is to avoid an aggregation on the full data. With an index on level(promotion_id, level, points), this should have better performance.
What I'm trying to achieve is to fetch the latest date of another column based on the same msisdn (if there exists more than one msisdn that is linked to other imsis). (You can assume imsi is more of a unique column)
(Tables are simplified for demonstration purposes)
I've two tables like the following:
operator table
+--------+--------+---------------------+
| imsi | msisdn | last_accessed |
+--------+--------+---------------------+
| 74583 | 004442 | 2018-04-05 16:20:32 |
+--------+--------+---------------------+
| 94210 | 023945 | 2017-02-13 11:27:14 |
+--------+--------+---------------------+
| 59123 | 004442 | 2018-07-15 05:24:55 |
+--------+--------+---------------------+
| 61234 | 089923 | 2018-07-21 16:13:29 |
+--------+--------+---------------------+
customer table
+--------+--------------+---------------------+
| imsi | company_id | business_plan |
+--------+--------------+---------------------+
| 74583 | FEX | yearly |
+--------+--------------+---------------------+
| 94210 | AOH | trial |
+--------+--------------+---------------------+
| 59123 | BIOI | monthly |
+--------+--------------+---------------------+
| 61234 | OOX | simple |
+--------+--------------+---------------------+
The following result is what I aim for. If I search for 74583 it should return 2018-07-15 05:24:55.
+--------+--------------+---------------------+----------------------+
| imsi | company_id | business_plan | last_accessed_date |
+--------+--------------+---------------------+----------------------+
| 74583 | FEX | yearly | 2018-07-15 05:24:55 |
+--------+--------------+---------------------+----------------------+
The following query returns almost what I try to achieve but does not return the latest date according to the table above.
SELECT
cust.imsi,
cust.company_id,
cust.business_plan,
CASE
WHEN
(
SELECT MAX(subop.last_accessed)
FROM operator subop
WHERE subop.msisdn = op.msisdn
GROUP BY subop.msisdn
HAVING COUNT(*) > 1
)
THEN
op.last_accessed
ELSE
'Never'
END
AS last_accessed_date
FROM customer cust
INNER JOIN operator op
ON cust.imsi = op.imsi
WHERE cust.imsi = '74583';
We can try doing this using a correlated subquery in the select clause:
SELECT
c.imsi,
c.company_id,
c.business_plan,
(SELECT MAX(t.last_accessed) FROM operator t
WHERE t.msisdn = o.msisdn) last_accessed_date
FROM customer c
INNER JOIN operator o
ON c.imsi = o.imsi
WHERE c.imsi = '74583';
Follow the link below for a SQLFiddle demo.
Demo
This query will return the last_accessed_date for every imsi:
select
o1.imsi,
o1.msisdn,
max(o2.last_accessed) as last_accessed_date
from
operator o1 inner join operator o2
on o1.msisdn = o2.msisdn
group by
o1.imsi,
o1.msisdn
(I am joining the operators table with itself to get the last accessed date based on the msisdn column). Then you can join this query with the customer table:
select
c.imsi,
c.company_id,
c.business_plan,
coalesce(l.last_accessed_date, 'Never') as last_accessed_date
from
customer c left join (
select
o1.imsi,
o1.msisdn,
max(o2.last_accessed) as last_accessed_date
from
operator o1 inner join operator o2
on o1.msisdn = o2.msisdn
group by
o1.imsi,
o1.msisdn
) l on c.imsi = l.imsi
it can then be written in some different ways, but I think this is the easier to understand.
Please see a fiddle here http://sqlfiddle.com/#!9/0f080c/1
Try this
SELECT
cust.imsi,
cust.company_id,
cust.business_plan,
(
SELECT MAX(last_accessed) FROM operator AS a WHERE a.msisdn = op.msisdn
) AS last_accessed_date
FROM customer cust
INNER JOIN operator op
ON cust.imsi = op.imsi
WHERE cust.imsi = '74583'
If I have a table of cases:
CASE_NUMBER | CASE_ID | STATUS | SUBJECT |
----------------------------------------------------------------
3108 | 123456 | Closed_Billable | Something Interesting
3109 | 325124 | Closed_Billable | Broken printer
3110 | 432432 | Open_Assigned | Email not working
And a table of calls:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
---------------------------------------------------------------
123456 | Held | 1 | 30 | Initial discussion
123456 | Cancelled | 0 | 0 | Walk user through
123456 | Held | 0 | 45 | Remote debug session
325124 | Held | 1 | 0 | Consultation
325124 | Held | 1 | 15 | Needs assessment
432432 | Held | 1 | 30 | Support call
And a table of meetings:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
-------------------------------------------------------
123456 | Held | 3 | 15 | On-site work
325124 | Held | 2 | 0 | Un-jam printer
432432 | Held | 1 | 0 | Reconnect network
How do I do a select with these parameters (this is not working code, obviously):
SELECT cases.case_number, cases.subject, calls.subject, meetings.subject
WHERE cases.status="Closed_Billable" AND (calls.status="Held" OR meetings.status="Held)
LEFT JOIN cases
ON cases.case_id = calls.parent_id
LEFT JOIN cases
ON cases.case_id = meetings.parent_id
and end up with a "faked" nested table like:
CASE_NUMBER | CASE SUBJECT | # CALLS | # MEETINGS | CALL SUBJECT | MEETING SUBJECT | DURATION (H) | DURATION (M) | TOTAL
-----------------------------------------------------------------------------------------------------------------------------------------
3108 | Something Interesting | 2 | 1 | | | | | 5.5H
| | | | Initial Discussion | | 1 | 30 |
| | | | Remote Debug Session | | 0 | 45 |
| | | | | On-site work | 3 | 15 |
3109 | Broken printer | 2 | 1 | | | | | 4.25H
| | | | Consultation | | 1 | 0 |
| | | | Needs assessment | | 1 | 15 |
| | | | | Un-jam printer | 2 | 0 |
I've tried joins and subqueries the best I can figure out, but I get repeated entries - for example, each Meeting in a Case will show say 3 times, once for each Call in that case.
I'm stumped! Obviously there's other fields I'm pulling here, and doing COUNTs of Calls and Meetings, and SUMs of their durations, but I'd be happy just to show a table/sub-table like this.
Is it even possible?
Thanks,
David.
Assembling a query result in the exact format you want is .. somewhat of a pain. It can be done, but presentation stuff like that is best left to the application.
That said, this will do what you want:
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.subject, '') `call subject`,
coalesce(meetings.subject, '') `meeting subject`,
case when calls.subject is not null then calls.dhour
when meetings.subject is not null then meetings.dhour
else ''
end dhour,
case when calls.subject is not null then calls.dmin
when meetings.subject is not null then meetings.dmin
else ''
end dhour,
coalesce(q3.total, '') total
from
(
select case_number, case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.1')
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.2')
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(dhour + m) total
from
(select parent_id, dhour, dmin / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, dhour, dmin / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
Note, i've renamed your duration fields because i dislike the parenthesis in them.
We have to mangle the case_id a little bit inside the query in order to be able to get you your blank rows / fields - those are what makes the query cumbersome
There's a demo here: http://sqlfiddle.com/#!9/d59d4/21
edited code to work with different schema in comment fiddle
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.name, '') `call subject`,
coalesce(meetings.name, '') `meeting subject`,
case when calls.name is not null then calls.duration_hours
when meetings.name is not null then meetings.duration_hours
else ''
end duration_hours,
case when calls.name is not null then calls.duration_minutes
when meetings.name is not null then meetings.duration_minutes
else ''
end duration_hours,
coalesce(q3.total, '') total
from
(
select case_number, id as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.1') as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.2') as case_id
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(duration_hours + m) total
from
(select parent_id, duration_hours, duration_minutes / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, duration_hours, duration_minutes / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
You can't really get final results like that without some seriously ugly "wrapper" queries, of this sort:
SET #prevCaseNum := 'blahdyblahnowaythisshouldmatchanything';
SET #prevCaseSub := 'seeabovetonotmatchanything';
SELECT IF(#prevCaseNum = CASE_NUMBER, '', CASE_NUMBER) AS CASE_NUMBER
, IF(#prevCaseNum = CASE_NUMBER AND #prevCaseSubject = CASE_SUBJECT, '', CASE_SUBJECT) AS CASE_SUBJECT
, etc.....
, #prevCaseNum := CASE_NUMBER AS prevCaseNum
, #prevCaseSubject = CASE_SUBJECT AS prevCaseSub
, etc....
FROM ( [the real query] ORDER BY CASE_NUMBER, etc....) AS trq
;
And then wrap all that with another select to strip the prevCase fields.
And even this still won't give you the blanks you want on the "upper right".
I have 3 tables and I am trying to join those tables with inner join. however when I use count(distinct column_id) it mysql through error which is
SQL syntax : check
for the right syntax to use near '(DISTINCT as_ticket.vehicle_id) FROM as_vehicle INNER JOIN as_ticket
My Query
SELECT
`as_vehicle`.`make`, `as_vehicle`.`model`, `as_odometer`.`value`
COUNT (DISTINCT `as_ticket`.`vehicle_id`)
FROM `as_vehicle`
INNER JOIN `as_ticket`
ON `as_vehicle`.`vehicle_id` = `as_ticket`.`vehicle_id`
INNER JOIN `as_odometer`
ON `as_odometer`.`vehicle_id` = `as_vehicle`.`vehicle_id`
WHERE `as_ticket`.`vehicle_id` = 7
ORDER BY `as_odometer`.`value`
DESC
Tbl as_vehicle
+------------+-------------+---------+
| vehicle_id |make | model |
+------------+-------------+---------|
| 1 | HYUNDAI | SOLARIS |
| 2 | A638EA15 | ACCENT |
+-------------+------------+---------+
Tbl as_odometer;
+------------+-------+
| vehicle_id | value |
+------------+-------+
| 1 | 10500 |
| 5 | 20000 |
| 1 | 20000 |
+------------+-------+
Tbl service
+-----------+------------+
| ticket_id | vehicle_id |
+-----------+------------+
| 1 | 1 |
| 2 | 1 |
+-----------+------------+
You forgot a comma before count.
SELECT `as_vehicle`.`make`, `as_vehicle`.`model`, `as_odometer`.`value`,
count(DISTINCT `as_ticket`.`vehicle_id`) // here ---^
First, you should not have a space after the count() and you have a missing comma (as already noted). More importantly, you don't have a group by, so your query will return one row.
And, because of the where clause, the value will always be "1". You have restricted the query to just one vehicle id.
I suspect the query you want is more like:
SELECT `as_vehicle`.`make`, `as_vehicle`.`model`, `as_odometer`.`value`
COUNT(*)
FROM `as_vehicle` INNER JOIN
`as_ticket`
ON `as_vehicle`.`vehicle_id` = `as_ticket`.`vehicle_id` INNER JOIN
`as_odometer`
ON `as_odometer`.`vehicle_id` = `as_vehicle`.`vehicle_id`
WHERE `as_ticket`.`vehicle_id` = 7
GROUP BY `as_vehicle`.`make`, `as_vehicle`.`model`, `as_odometer`.`value`
ORDER BY `as_odometer`.`value` DESC;
Also, you should learn to use table aliases and all those backquotes don't help the query.
I have two tables: contacts and client_profiles. A contact has many client_profiles, where client_profiles has foreign key contact_id:
contacts:
mysql> SELECT id,first_name, last_name FROM contacts;
+----+-------------+-----------+
| id | first_name | last_name |
+----+-------------+-----------+
| 10 | THERESA | CAMPBELL |
| 11 | donato | vig |
| 12 | fdgfdgf | gfdgfd |
| 13 | some random | contact |
+----+-------------+-----------+
4 rows in set (0.00 sec)
client_profiles:
mysql> SELECT id, contact_id, created_at FROM client_profiles;
+----+------------+---------------------+
| id | contact_id | created_at |
+----+------------+---------------------+
| 6 | 10 | 2014-10-09 17:17:43 |
| 7 | 10 | 2014-10-10 11:38:01 |
| 8 | 10 | 2014-10-10 12:20:41 |
| 9 | 10 | 2014-10-10 12:24:19 |
| 11 | 12 | 2014-10-10 12:35:32 |
+----+------------+---------------------+
I want to get the latest client_profiles for each contact. That means There should be two results. I want to use subqueries to achieve this. This is the subquery I came up with:
SELECT `client_profiles`.*
FROM `client_profiles`
INNER JOIN `contacts`
ON `contacts`.`id` = `client_profiles`.`contact_id`
WHERE (client_profiles.id =
(SELECT `client_profiles`.`id` FROM `client_profiles` ORDER BY created_at desc LIMIT 1))
However, this is only returning one result. It should return client_profiles with id 9 and 11.
What is wrong with my subquery?
It looks like you were trying to filter twice on the client_profile table, once in the JOIN/ON clause and another time in the WHERE clause.
Moving everything in the where clause looks like this:
SELECT `cp`.*
FROM `contacts`
JOIN (
SELECT
`client_profiles`.`id`,
`client_profiles`.`contact_id`,
`client_profiles`.`created_at`
FROM `client_profiles`
ORDER BY created_at DESC
LIMIT 1
) cp ON `contacts`.`id` = `cp`.`contact_id`
Tell me what you think.
Should be something like maybe:
SELECT *
FROM `client_profiles`
INNER JOIN `contacts`
ON `contacts`.`id` = `client_profiles`.`contact_id`
GROUP BY `client_profiles`.`contact_id`
ORDER BY created_at desc;
http://sqlfiddle.com/#!2/a3f21b/9
You need to prequery the client profiles table grouped by each contact.. From that, re-join to the client to get the person, then again to the client profiles table based on same contact ID, but also matching the max date from the internal prequery using max( created_at )
SELECT
c.id,
c.first_name,
c.last_name,
IDByMaxDate.maxCreate,
cp.id as clientProfileID
from
( select contact_id,
MAX( created_at ) maxCreate
from
client_profiles
group by
contact_id ) IDByMaxDate
JOIN contacts c
ON IDByMaxDate.contact_id = c.id
JOIN client_profiles cp
ON IDByMaxDate.contact_id = cp.contact_id
AND IDByMaxDate.maxCreate = cp.created_at