Selecting 1 Instance of a Client's family in MySQL - mysql

What I need to return from the query is a list of Heads of Households who have visited in the past year and all of their relatives. Everyone is in the Client table which is joined to the other tables when queried.
The problem is that the query returns a "set" of Family members for each unique DateVisited. I'm hoping to get a set of results that looks like this (I omitted some columns for clarity):
Head of household Relative/child
---------------------------- ---------------------
John Smith - Katie Smith
John Smith Joe Smith
Tim Jones Mike Jones
Tim Jones Sally Jones
Kevin Barnett Corey Barnett
Kevin Barnett Cara Barnett
Kevin Barnett Austin Barnett
SELECT f.HeadOfHouseholdID,
CONCAT( c.lastname, ', ', c.firstname ) AS 'HName',
v.Datevisited, f.RelationshipID, c2.ClientID,
CONCAT( c2.lastname, ', ', c2.firstname ) AS 'Relative Name'
FROM client c
INNER JOIN clientfamily f ON c.ClientID = f.HeadOfHouseholdID
JOIN visits v ON c.clientid = v.clientID
JOIN client c2 ON c2.clientid = f.relativeID
WHERE v.datevisited BETWEEN CURDATE( ) - INTERVAL 1 YEAR AND CURDATE( )
![Query Results][1]

A coworker figured it out:
SELECT f.HeadOfHouseholdID 'HOHID',
CONCAT( c.Lastname, ', ', c.FirstName ) AS 'Head of Household',
c.PhoneNumber AS 'HOH Phone',
f.RelativeID,
CONCAT( C2.Lastname, ', ', C2.FirstName ) AS 'Relative',
Relationship.Description AS 'Relation',
C2.PhoneNumber AS 'Relative Phone', C2.DOB,
(
(
DATE_FORMAT( NOW( ) , '%Y' ) - DATE_FORMAT( C2.DOB, '%Y' )
) -
( DATE_FORMAT( NOW( ) , '00-%m-%d' ) < DATE_FORMAT( C2.DOB, '00-%m-%d' ) )
) AS Age
FROM client c
INNER JOIN clientfamily f ON c.clientid = f.headofhouseholdid
JOIN (
SELECT ClientID, MAX( DateVisited ) AS 'DateVisited'
FROM Visits
GROUP BY ClientID
) v ON c.clientid = v.clientid
JOIN client c2 ON c2.clientid = f.relativeid
JOIN Relationship ON f.RelationshipID = Relationship.RelationshipID
WHERE v.datevisited
BETWEEN CURDATE( ) - INTERVAL 1 YEAR
AND CURDATE( )

Related

how do i combine results in mysql from 3 different tables with unique id

Here is the situation i have 3 tables lets say
cust_data, deposit_data, transaction_info:
in cust_data I have Firstname, Lastname, Email ,Phone, customer_id
in deposit_data I have deposit_id, deposit_data,date,amount
in transaction_info I have customer_id,deposit_id, email, transaction_date,processor
Now I need to list all info of the customer with all the deposits he made for example:
cust_info : (Firstname, Lastname, Email ,Phone, customer_id)
1) John , smith , johnsmith#bla.com, 11111111, 111-111-111
2) Sara , sugar , sarasugar#lala.com, 22222222, 222-222-222
Deposit_data: (deposit_id, deposit_data,date,amount)
1) 187823 , " some processing info card number...." , 10/10/2019 , 100
2) 908202 , " some processing info card number...." , 11/11/2019 , 90
3) 323243 , " some processing info card number...." , 12/12/2019 , 100
transaction_info: (customer_id,deposit_id, email, transaction_date,processor)
1) 111-111-111 , 908202 , johnsmith#bla.com , 11/11/2019 , googlepay
2) 111-111-111 , 187823 , johnsmith#bla.com , 10/10/2019 , visa
3) 222-222-222 , 323243 , sarasugar#lala.com, 12/12/2019 , visa
Now I want to make 1 query that will show me all the deposits the customer made so the result will look like:
customer_id,Firstname, Lastname, Email ,Phone, amount(bydate) :
111-111-111 John , smith , johnsmith#bla.com ,11111111 , (10/10/2019 : 100 | 10/10/2019 : 90)
Better if I can make everything in the same statement, I just dont know how to combine all 3.
GROUP BY GROUP_CONCAT INNER JOIN, gives you the answer
Dates should always be saved in MySQL format, or else, when you have to use functions with it , you have to convert them always
CREATE TABLE cust_info (
`Firstname` VARCHAR(4),
`Lastname` VARCHAR(5),
`Email` VARCHAR(18),
`Phone` INTEGER,
`customer_id` VARCHAR(11)
);
INSERT INTO cust_info
(`Firstname`, `Lastname`, `Email`, `Phone`, `customer_id`)
VALUES
('John', 'smith', 'johnsmith#bla.com', '11111111', '111-111-111'),
('Sara', 'sugar', 'sarasugar#lala.com', '22222222', '222-222-222');
CREATE TABLE Deposit_data (
`deposit_id` INTEGER,
`deposit_data` VARCHAR(39),
`date` VARCHAR(10),
`amount` INTEGER
);
INSERT INTO Deposit_data
(`deposit_id`, `deposit_data`, `date`, `amount`)
VALUES
('187823', ' some processing info card number....', '10/10/2019', '100'),
('908202', ' some processing info card number....', '11/11/2019', '90'),
('323243', ' some processing info card number....', '12/12/2019', '100');
CREATE TABLE transaction_info (
`customer_id` VARCHAR(11),
`deposit_id` INTEGER,
`email` VARCHAR(18),
`transaction_date` varchar(10),
`processor` VARCHAR(9)
);
INSERT INTO transaction_info
(`customer_id`, `deposit_id`, `email`, `transaction_date`, `processor`)
VALUES
('111-111-111', '908202', 'johnsmith#bla.com', '11/11/2019', 'googlepay'),
('111-111-111', '187823', 'johnsmith#bla.com', '10/10/2019', 'visa'),
('222-222-222', '323243', 'sarasugar#lala.com', '12/12/2019', 'visa');
SELECT
ci.customer_id,
ci.Firstname,
ci.Lastname,
ci.Email,
ci.Phone
,
group_concat(
dd.date, ' : ', dd.amount
order by dd.date
separator ' | ') 'amount(bydate)'
FROM
cust_info ci
INNER JOIN
transaction_info ti ON ti.customer_id = ci.customer_id
INNER JOIN
Deposit_data dd ON dd.deposit_id = ti.deposit_id
GROUP BY ci.customer_id , ci.Firstname , ci.Lastname , ci.Email , ci.Phone
customer_id | Firstname | Lastname | Email | Phone | amount(bydate)
:---------- | :-------- | :------- | :----------------- | -------: | :---------------------------------
111-111-111 | John | smith | johnsmith#bla.com | 11111111 | 10/10/2019 : 100 | 11/11/2019 : 90
222-222-222 | Sara | sugar | sarasugar#lala.com | 22222222 | 12/12/2019 : 100
db<>fiddle here
You can use string aggregation. A correlated subquery might be more efficient than joins and outer aggregation:
select
c.*,
(
select group_concat(
d.date, ' : ', d.amount
order by d.date
separator ' | '
from transaction_info t
inner join deposit_data d on d.deposit_id = t.deposit_id
where t.customer_id = c.customer_id
)
from cust_info c
You can add a where clause to the outer query to filter on a given customer if needed.
You can use next query as solution:
select
ci.customer_id,
Firstname,
Lastname,
ci.Email,
Phone,
group_concat(CONCAT(dd.date, ' : ', dd.amount)SEPARATOR ' | ') deposit
from cust_info ci
left join transaction_info ti on ci.customer_id = ti.customer_id
join Deposit_data dd on dd.deposit_id = ti.deposit_id
group by ci.customer_id, Firstname, Lastname, ci.Email, Phone
;
Here you can look working example
You need an inner join select statment.
SELECT * FROM cust_info c INNER JOIN transaction_info t ON c.cusomter_id = t.customer_id INNER JOIN deposit_data d ON d.deposit_id = t.deposit_id
This should be work fine

complex query - look back on same table data to output results in mysql (VERY slow query)

I'm trying to optimize a query, which as the db is growing, its performance is severely lacking.
Background: we are trying to find a list of users who have taken a course and their credential is now due to be renewed (or has not renewed). In searching the query we have to have a look into the registration table (which is the same table that holds all their registration history) and find records where they have not renewed. (Each time the client takes a course they have a registration record added.) The query I'm wanting to optimize looks to see if they've (client) taken the same course type on a date/time after the last class (of same type) they took. If there is no record it should result row(s) that they didn't renew their course. it sounds easy, but, as you know, when you're in the heat of writing a query it gets very complex--and even more so once the db has grown to be so large that it takes almost 5-6 minutes to find the data. So, I'm asking for help on how I can optimize the efforts of my predecessor, below.
Here is the query, thus far (don't laugh, it wasn't started by me--I took over the project).
I have no clue where to begin with optimizing this MySQL. I think it needs to have select statements within the JOINS, but I'm at your mercy to direct me as to where to start! (I"m not a db guy, but offered to take a look and see where we can fix this).
Thanks a million for reading.
Lee
SELECT
r.GUID AS `A/C #`,
concat( a.AttendeeLastName, ', ', a.AttendeeFirstName ) AS Full Name (Last, First),
r.CourseExpirationDateFull AS `Exp Date`,
mtype_master_abbrev AS Course,
a.EmailName AS Email,
r.EventID,
r.EventTypeMasterID,
m.type_master_name,
IF( ( r.CourseExpirationDateFull < curdate( ) ), 'Expired', 'Valid' ) AS Status,
e.StartDateTime,
( to_days( curdate( ) ) - to_days( r.ExpNoticeSent ) ) AS Last Notice,
r.AttendeeID,
a.AttendeeCredentials,
r.RegistrationID,
r.RenewedExternalYYYY,
r.ExpNoticeSent,
q.RenewedRegID,
rs.reg_status_name AS `Reg Status`,
( to_days( r.CourseExpirationDateFull ) - to_days( curdate( ) ) ) AS Days2Exp,
a.flgReturnEmail,
a.flgSendEmail,
r.reg_type_ID,
a._usr_flg_do_not_call,
a.flgPrintLetter
e.EventTypeMasterID AS MasterID,
c.Last: yy-mm-dd - by - topic AS LastComm,
r.reg_renewal_status_id
FROM
vjgzuqrr_wtsql.registration r LEFT JOIN vjgzuqrr_wtsql.events ON ( r.EventID = events.EventID ) LEFT JOIN
vjgzuqrr_wtsql.attendees a ON a.ID = r.GUID LEFT JOIN
vjgzuqrr_wtsql.tbl_crs_type_master m ON r.EventTypeMasterID = m.ID_crs_type_master LEFT JOIN
vjgzuqrr_wtsql.qryrenreg q ON r.RegistrationID = q.OrigRegID LEFT JOIN
vjgzuqrr_wtsql.tbl_reg_status rs ON rs.ID_reg_status = r.RegistrationStatus LEFT JOIN
vjgzuqrr_wtsql.v_last_contact c ON c.registrationid = r.RegistrationID
WHERE
r.Role = 1
AND r.reg_type_ID IN ( 1, 2 )
AND r.CompletionStatus IN ( 9, 8 )
AND r.r IN ( 1, 14, 9 )
AND ( r.EventTypeMasterID IS NOT NULL OR r.EventTypeMasterID = 17 )
AND r.flgDelete = 0
AND r.flgTest = 0
AND e.flgDelete = 0
AND e.flgTestCourse = 0
AND e.flgDelete = 0
AND a.flgTest = 0
AND isnull( q.RenewedRegID )
AND a.flgReturnEmail = 0
AND m.type_master_abbrev NOT IN ( 'EKGPHARM', 'IVCERT', 'sem', 'fam&friends', 'cccc' )
Edit to include Explain:
Sorry im a bit slow, mysql,
This does not speed anything up ( i think, but it may help a bit), but it should help in reading it in a non-mindbreaking way. (hopefully this will also help others look at it.)
SELECT
r.GUID AS `A/C #`,
concat( a.AttendeeLastName, ', ', a.AttendeeFirstName ) AS Full Name (Last, First),
r.CourseExpirationDateFull AS `Exp Date`,
mtype_master_abbrev AS Course,
a.EmailName AS Email,
r.EventID,
r.EventTypeMasterID,
m.type_master_name,
IF( ( r.CourseExpirationDateFull < curdate( ) ), 'Expired', 'Valid' ) AS Status,
e.StartDateTime,
( to_days( curdate( ) ) - to_days( r.ExpNoticeSent ) ) AS Last Notice,
r.AttendeeID,
a.AttendeeCredentials,
r.RegistrationID,
r.RenewedExternalYYYY,
r.ExpNoticeSent,
q.RenewedRegID,
rs.reg_status_name AS `Reg Status`,
( to_days( r.CourseExpirationDateFull ) - to_days( curdate( ) ) ) AS Days2Exp,
a.flgReturnEmail,
a.flgSendEmail,
r.reg_type_ID,
a._usr_flg_do_not_call,
a.flgPrintLetter
e.EventTypeMasterID AS MasterID,
c.Last: yy-mm-dd - by - topic AS LastComm,
r.reg_renewal_status_id
FROM
vjgzuqrr_wtsql.registration r LEFT JOIN
vjgzuqrr_wtsql.events e ON r.EventID = e.EventID LEFT JOIN
vjgzuqrr_wtsql.attendees a ON a.ID = r.GUID LEFT JOIN
vjgzuqrr_wtsql.tbl_crs_type_master m ON r.EventTypeMasterID = m.ID_crs_type_master LEFT JOIN
vjgzuqrr_wtsql.qryrenreg q ON r.RegistrationID = q.OrigRegID LEFT JOIN
vjgzuqrr_wtsql.tbl_reg_status rs ON rs.ID_reg_status = r.RegistrationStatus LEFT JOIN
vjgzuqrr_wtsql.v_last_contact c ON c.registrationid = r.RegistrationID
WHERE
r.Role = 1
AND r.reg_type_ID IN ( 1, 2 )
AND r.CompletionStatus IN ( 9, 8 )
AND r.r IN ( 1, 14, 9 )
AND ( r.EventTypeMasterID IS NOT NULL OR r.EventTypeMasterID = 17 )
AND r.flgDelete = 0
AND r.flgTest = 0
AND e.flgDelete = 0
AND e.flgTestCourse = 0
AND e.flgDelete = 0
AND a.flgTest = 0
AND isnull( q.RenewedRegID )
AND a.flgReturnEmail = 0
AND m.type_master_abbrev NOT IN ( 'EKGPHARM', 'IVCERT', 'sem', 'fam&friends', 'cccc' )

Mismatched MySQL dates evaluate to equal

I have this query:
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime
from EventClickIns eci
join WebEvents w
on eci.WebEventID = w.ID
where eci.UserID = 493
and eci.WebEventID <> 10
and eci.InvitationID <> 175
and date(eci.ClickInDate) = date(now())
and (RecurringEventTime = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00')
limit 1
And I get this record as the result:
EventName EventLocation RecurringEventTime OneTimeDateTime
========================================================================
Evt ABC 123 Anystreet 2018-03-20 12:00:00 NULL
It's baffling why I am getting a matching record when the RecurringEventTime in my where clause is different from the RecurringEventTime in the matching record. OneTimeDateTime is null, so that cannot be matching.
What am I missing?
Thanks to hints from Sam and Solarflare, I got it figured out:
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime
from EventClickIns eci
join WebEvents w
on eci.WebEventID = w.ID
where eci.UserID = 493
and eci.WebEventID <> 10
and eci.InvitationID <> 175
and date(eci.ClickInDate) = date(now())
and (CONCAT(CURDATE(), ' ', w.RecurringEventTime) = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00')
limit 1

SQL MULTIPLE COUNT AND JOIN

I'm trying to do something like this:
User_guid | Post Message | Discussion board | Total |
1 | 300 | 25 | 325 |
2 | 15 | 185 | 200 |
3 | 100 | 203 | 303 |
but I don't know how.
I have the Post Message result with this Query:
SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
and I have the Discussion result with this Query:
SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
But I don't know how to join these results as rows of the users_entity table. The total interactions should be the sum of the post message and discussion board values. Someone can help me? Thanks.
It's hard to tell without table structures and sample data (which is why you're getting the down votes), but you probably need something like this:
SELECT
users_entity.name,
users_entity.username,
COUNT(CASE WHEN river.action_type = 'create' AND river.view = 'river/object/thewire/create' THEN 1 END) AS Post_Message,
COUNT(CASE WHEN river.action_type = 'reply' THEN 1 END) AS Discussion_Board
FROM river
JOIN users_entity ON users_entity.guid = river.subject_guid
GROUP BY
users_entity.name,
users_entity.username
ORDER BY ???
I'm not sure which value you want to ORDER BY with the combined query.
Maybe this works for you:
SELECT data1.username, data1.count1, data2.count2, data1.count1+data2.count2 from
(
SELECT COUNT( * ) as 'count1' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
) data1,
(
SELECT COUNT( * ) as 'count2' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) data2 where data1.username = data2.username
You could try joining the two queries like this
SELECT river_create.Quantidade AS create_q, river_reply.Quantidade AS reply_q, river_create.full_name, river_create.u_name, river_create.user_id
FROM
(SELECT COUNT( * ) as 'Quantidade' , users_entity.name AS full_name, users_entity.username AS u_name, users_entity.guid AS user_id
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) AS river_create
INNER JOIN
(SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username, users_entity.guid as reply_user_id
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) AS river_reply
ON river_create.user_id = river_reply.reply_user_id

Getting SUM and JOIN to cooperate in a specific query

I'm stuck with a certain query, it seems to be simple but it seems I might have to split it in different ways to get it to accomplish what I want to do. I tried combining, recombining and I could use some guidance.
Here's a sample table, the output and expected output.
Shop_Table
shop_id | item_type | date
1 soaps 2000-01-01
2 food 2000-01-02
3 appliances 2000-01-03
4 electronics 2000-01-10
5 furniture 2000-01-13
6 misc. 2000-01-15
Instance_Table
instanceid| shop_id | firstname | lastname | number_of_items
11 3 jane doe 2
22 2 jane doe 3
33 1 jane doe 5
44 4 jane doe 6
55 6 jane doe 1
66 5 jane doe 2
The Query
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
INNER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Which returns:
item_type | item_sum
indexpensives | 8
appliances | 2
Output
Which is great so far, and is where I'm stuck. What I really want is the following output:
Expected Output
item_type | item_sum
inexpensives | 8
appliances | 2
electronics | 0
furniture | 0
misc. | 0
I think it's pretty straight forward what I want to do. What's making it a little tough is that it has a little bit of everything.
Grouping and combining sum results (soaps and food into one result)
Returning 0 for the rest of the item types when the date is outside the specified range.
Update:
Turned out a little more complicated than I thought, but it's what I wanted. Thank you everyone for helping out.
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
)
Change your query to this and you should get the totals you're looking for:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
SUM(Person.number_of_items) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Explanation
An outer join will select all rows from one table and matching rows from another. So, a LEFT OUTER JOIN will select all rows from the table on the left side of the join (Shop_Table in this case) and matching rows from the table on the right side of the join (Instance_Table in this case).
First of all replace your inner join with a left join and then make sure you coalesce the sum with a 0, like this:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Edit:
After the requirement changes, this should be the final query:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
) as FinalType,
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY FinalType