Using CASE where need data from another record - mysql

I'm using a CASE statement in a SELECT statement that arguably should be broken up into smaller parts but I'm one field off completing this task and have passed the point of no return.
Without dropping the whole query in here, the gist of the issue is this:
SELECT
Dogs,
Cats,
COUNT(DISTINCT CASE WHEN my.my_id = 2765 THEN my.data END) AS CountAccounts,
COUNT(DISTINCT CASE WHEN my.my_id = 3347 THEN my.data END) AS CountUpgradedAccounts
my refers to a table that is a lookup table of name value pairs. When my.my_id = 3347 that signifies an "upgraded" account and the data point is the date that the account was upgraded. When my.my_id = 2765 that signifies account creation and the corresponding data point is the accountID.
my It looks like this:
UserID | my_id | Data
374 | 2765 | 8826
487 | 3347 | 2013-09-01
662 | 2765 | 8826
321 | 2765 | 9213
722 | 3347 | 2014-10-14
852 | 2765 | 8826
487 | 2765 | 9213
When my_id = 2765, I'd like the distinct number of accountIDs that it relates to. In the table above that is 2: Accounts 8826 and 9213.
I know this would be really simple if I was pulling data from my only. But my is woven into my query in a way that complicates things.
In fact, here is the query, perhaps the problem will be easier to see. Note the last field being selected in the SELECT statement is the problem. I don;t want to count distinct dates, I want to count distinct accountIDs that have upgraded.:
SELECT
sub.name AS ARName,
sub.desc AS ARDescription,
m.name AS MessageName,
m.subj AS MessageDescription,
clk.type AS EventType,
COUNT(DISTINCT clk.eid) AS CountAdmins,
COUNT(DISTINCT CASE WHEN my.my_id = 3347 THEN clk.eid END) AS CountUpgradeAdmins,
COUNT(DISTINCT CASE WHEN my.my_id = 2765 THEN my.data END) AS CountAccounts,
COUNT(DISTINCT CASE WHEN my.my_id = 3347 THEN my.data END) AS CountUpgradedAccounts # <-- THIS LINE IS THE PROBLEM
FROM
bata.sseq seq
INNER JOIN bata.messages m
ON m.id = seq.mid
INNER JOIN bm_arc.clicks208 clk
ON clk.camp = seq.camp
INNER JOIN bemails.cid cid
ON cid.id = clk.eid
INNER JOIN bonfig.sub
ON sub.id = seq.sid
LEFT JOIN bemails.my208 my
ON cid.id = my.eid AND (my_id = 3347 OR my_id = 2765) # only return people who upgraded and accountIDs
WHERE
seq.cid = 208
AND
sub.desc REGEXP '^Home pg free trail (A|B)'
GROUP BY
ARName,
ARDescription,
MessageName,
MessageDescription,
EventType
I've found trying to word this question challenging so sorry if what I'm asking is not clear. If there's any more info I can add let me know.
Following discussion, what I'm asking for in other words:
"For each instance of 3347 get the corresponding instances of UserID
and with those UserIDs the count of distinct corresponding datapoints
in my.data WHERE my.my_id = 2765"

Maybe try changing the problem line to, select UserId, not data:
SELECT
sub.name AS ARName,
sub.desc AS ARDescription,
m.name AS MessageName,
m.subj AS MessageDescription,
clk.type AS EventType,
COUNT(DISTINCT clk.eid) AS CountAdmins,
COUNT(DISTINCT CASE WHEN my.my_id = 3347 THEN clk.eid END) AS CountUpgradeAdmins,
COUNT(DISTINCT CASE WHEN my.my_id = 2765 THEN my.data END) AS CountAccounts,
COUNT(DISTINCT my2.data ) AS CountUpgradedAccounts
FROM
bata.sseq seq
INNER JOIN bata.messages m
ON m.id = seq.mid
INNER JOIN bm_arc.clicks208 clk
ON clk.camp = seq.camp
INNER JOIN bemails.cid cid
ON cid.id = clk.eid
INNER JOIN bonfig.sub
ON sub.id = seq.sid
LEFT JOIN bemails.my208 my
ON cid.id = my.eid AND (my.my_id = 3347 OR my.my_id = 2765) # only return people who upgraded and
LEFT JOIN bemails.my208 my2
ON my2.my_id = 2765 and my2.userID = my.userID and my.my_id=3347 #get the accounts that the user belongs to
WHERE
seq.cid = 208
AND
sub.desc REGEXP '^Home pg free trail (A|B)'
GROUP BY
ARName,
ARDescription,
MessageName,
MessageDescription,
EventType

Related

COnditional JOIN query in MySQL

I have two mysql tables
user:
|--------------------------------|
| id | name | type | ruser_type |
|--------------------------------|
| 1 | Admin | a | |
| 2 | | r | c |
|--------------------------------|
customer
|-------------------------|
| id | name | user_id |
|-------------------------|
| 1 | Sam | 2 |
|-------------------------|
If user.type is 'a' or 's', then its admin user whose name is in user table.
If user.type is 'r' and ruser_type is 'c', then its regular user which has a relation in customer table where customer.user_id = user.id
I want a query which would run a conditional join.
If user.type is 'a' or 's', then name would be fetched from user table.
If user.type is 'r' and and ruser_type is 'c', then name would be fetched from customer table with the JOIN condition customer.user_id = user.id.
For this, I have written a query like this:-
SELECT users.fname as adminFname, customers.fname as customerFname, users.type FROM users
LEFT JOIN customers ON (customers.user_id = users.id AND
(
(users.type = 'r' AND users.ruser_type = 'c')
OR users.type = 'a'
OR users.type = 's'
)
)
WHERE users.id = 1
Is there any possibility to optimize the query more?
Also, how can I write this query using Laravel eloquent?
FWIW, I find this marginally easier to read...
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT
JOIN customers c
ON c.user_id = u.id
WHERE u.id = 1
AND (
(u.type = 'r' AND u.ruser_type = 'c')
OR (u.type IN('a','s'))
)
I have written two sql query hope this will help you
SELECT CASE CU.type WHEN 'a' OR 's' THEN CU.name END AS name,
CASE WHEN CU.type = 'r' AND CU.ruser_type = 'c' THEN CR.name END AS cust_name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this,
name cust_name type
Sam r
Admin a
and i have wrote another query like this,
SELECT CASE WHEN CU.type = 'a' OR 's' THEN CU.name ELSE CR.name END AS name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this
name type
Sam r
Admin a
DB File Link
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b02d931b68a4f70d8b4a84144c60a572
This will give you required result for all users:
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)
Add where condition as required.
You can even simplify it further to get common firstName column in output:
SELECT COALESCE(u.fname, c.fname) firstName, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)

How can I optimize my sql code?

I have following tables
contacts
contact_id | contact_slug | contact_first_name | contact_email | contact_date_added | company_id | contact_is_active | contact_subscribed | contact_last_name | contact_company | contact_twitter
contact_campaigns
contact_campaign_id | contact_id | contact_campaign_created | company_id | contact_campaign_sent
bundle_feedback
bundle_feedback_id | bundle_id, contact_id | company_id | bundle_feedback_rating | bundle_feedback_favorite_track_id | bundle_feedback_supporting | campaign_id
bundles
bundle_id | bundle_name | bundle_created | company_id | bundle_is_active
tracks
track_id | company_id | track_title
I wrote this query, but it works slowly, how can I optimize this query to make it faster ?
SELECT SQL_CALC_FOUND_ROWS c.contact_id,
c.contact_first_name,
c.contact_last_name,
c.contact_email,
c.contact_date_added,
c.contact_company,
c.contact_twitter,
concat(c.contact_first_name," ", c.contact_last_name) AS fullname,
c.contact_subscribed,
ifnull(icc.sendCampaignsCount, 0) AS sendCampaignsCount,
ifnull(round((ibf.countfeedbacks/sendCampaignsCount * 100),2), 0) AS percentFeedback,
ifnull(ibf.bundle_feedback_supporting, 0) AS feedbackSupporting
FROM contacts AS c
LEFT JOIN
(SELECT c.contact_id,
count(cc.contact_campaign_id) AS sendCampaignsCount
FROM contacts AS c
LEFT JOIN contact_campaigns AS cc ON cc.contact_id = c.contact_id
WHERE c.company_id = '876'
AND c.contact_is_active = '1'
AND cc.contact_campaign_sent = '1'
GROUP BY c.contact_id) AS icc ON icc.contact_id = c.contact_id
LEFT JOIN
(SELECT bf.contact_id,
count(*) AS countfeedbacks,
bf.bundle_feedback_supporting
FROM bundle_feedback bf
JOIN bundles b
JOIN contacts c
LEFT JOIN tracks t ON bf.bundle_feedback_favorite_track_id = t.track_id
WHERE bf.bundle_id = b.bundle_id
AND bf.contact_id = c.contact_id
AND bf.company_id='876'
GROUP BY bf.contact_id) AS ibf ON ibf.contact_id = c.contact_id
WHERE c.company_id = '876'
AND contact_is_active = '1'
ORDER BY percentFeedback DESC LIMIT 0, 25;
I have done 2 improvements
1) Removed the contacts which is getting joined unnecessarily twice and put the condition at the final where condition.
2) Removed as per SQL_CALC_FOUND_ROWS
Which is fastest? SELECT SQL_CALC_FOUND_ROWS FROM `table`, or SELECT COUNT(*)
SELECT c.contact_id,
c.contact_first_name,
c.contact_last_name,
c.contact_email,
c.contact_date_added,
c.contact_company,
c.contact_twitter,
concat(c.contact_first_name," ", c.contact_last_name) AS fullname,
c.contact_subscribed,
ifnull(icc.sendCampaignsCount, 0) AS sendCampaignsCount,
ifnull(round((ibf.countfeedbacks/sendCampaignsCount * 100),2), 0) AS percentFeedback,
ifnull(ibf.bundle_feedback_supporting, 0) AS feedbackSupporting
FROM contacts AS c
LEFT JOIN
(SELECT cc.contact_id,
count(cc.contact_campaign_id) AS sendCampaignsCount
FROM contact_campaigns
WHERE cc.contact_campaign_sent = '1'
GROUP BY cc.contact_id) AS icc ON icc.contact_id = c.contact_id
LEFT JOIN
(SELECT bf.contact_id,
count(*) AS countfeedbacks,
bf.bundle_feedback_supporting
FROM bundle_feedback bf
JOIN bundles b
LEFT JOIN tracks t ON bf.bundle_feedback_favorite_track_id = t.track_id
WHERE bf.bundle_id = b.bundle_id
GROUP BY bf.contact_id) AS ibf ON ibf.contact_id = c.contact_id
WHERE c.company_id = '876' and c.contact_is_active = '1'
First, you are not identifying any indexes you have to optimize the query. That said, I would ensure you have at least the following composite / covering indexes.
table index
contacts ( company_id, contact_is_active )
contact_campaigns ( contact_id, contact_campaign_sent )
bundle_feedback ( contact_id, bundle_feedback_supporting )
Next, as noted in other answer, unless you really need how many rows qualified, remove the "SQL_CALC_FOUND_ROWS".
In your first left-join (icc), you do a left-join on contact_campaigns (cc), but then throw into your WHERE clause an "AND cc.contact_campaign_sent = '1'" which turns that into an INNER JOIN. At the outer query level, these would result in no matching record and thus NULL for your percentage calculations.
In your second left-join (ibf), you are doing a join to the tracks table, but not utilizing anything from it. Also, you are joining to the bundles table but not using anything from there either -- unless you are getting multiple rows in the bundles and tracks tables which would result in a Cartesian result and possibly overstate your "CountFeedbacks" value. You also do not need the contacts table as you are not doing anything else with it, and the feedback table has the contact ID basis your are querying for. Since that is only grouped by the contact_id, your "bf.bundle_feedback_supporting" is otherwise wasted. If you want counts of feedback, just count from that table per contact ID and remove the rest. (also, the joins should have the "ON" clauses instead of within the WHERE clause for consistency)
Also, for your supporting feedback, the data type and value are unclear, so I implied as a Yes or No and have a SUM() based on how many are supporting. So, a given contact may have 100 records but only 37 are supporting. This gives you 1 record for the contact having BOTH values 100 and 37 respectively and not lost in a group by based on the first entry found for the contact.
I would try to summarize your query to below:
SELECT
c.contact_id,
c.contact_first_name,
c.contact_last_name,
c.contact_email,
c.contact_date_added,
c.contact_company,
c.contact_twitter,
concat(c.contact_first_name," ", c.contact_last_name) AS fullname,
c.contact_subscribed,
ifnull(icc.sendCampaignsCount, 0) AS sendCampaignsCount,
ifnull(round((ibf.countfeedbacks / icc.sendCampaignsCount * 100),2), 0) AS percentFeedback,
ifnull(ibf.SupportCount, 0) AS feedbackSupporting
FROM
contacts AS c
LEFT JOIN
( SELECT
c.contact_id,
count(*) AS sendCampaignsCount
FROM
contacts AS c
JOIN contact_campaigns AS cc
ON c.contact_id = cc.contact_id
AND cc.contact_campaign_sent = '1'
WHERE
c.company_id = '876'
AND c.contact_is_active = '1'
GROUP BY
c.contact_id) AS icc
ON c.contact_id = icc.contact_id
LEFT JOIN
( SELECT
bf.contact_id,
count(*) AS countfeedbacks,
SUM( case when bf.bundle_feedback_supporting = 'Y'
then 1 else 0 end ) as SupportCount
FROM
contacts AS c
JOIN bundle_feedback bf
ON c.contact_id = bf.contact_id
WHERE
c.company_id = '876'
AND c.contact_is_active = '1'
GROUP BY
bf.contact_id) AS ibf
ON c.contact_id = ibf.contact_id
WHERE
c.company_id = '876'
AND c.contact_is_active = '1'
ORDER BY
percentFeedback DESC LIMIT 0, 25;

mysql: Group Concat with joined tables and values that aren't unique

I have an issue where I need to aggregate and concatenate multiple row data into single row output. I understand the tables are a problem in that their is no unique index, but I need to do this at the query level instead of the scripting level and I can't touch the database structure.
Here we go:
table: characteristics
id code_a code_b
-------------------------------
2201 CHAU AIRS
2201 CHAU PELC
2201 PROX AUTO
2201 PROX HOP`
table: characteristics_types
code description
-------------------------------
CHAU Heating System
PROX Nearby
table: characteristics_sub_types
code_a code description
-------------------------------
CHAU AIRS Forced Air
CHAU PELC Baseboard
PROX AUTO Highway
PROX HOP Hospital
Result required:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard Highway, Hospital
Not working:
SELECT id,
(case when C.code_a='CHAU' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Heating System,
(case when C.code_a='PROX' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join characteristics_sub_types STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id,C.code_a
I am getting the following results:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard NULL
2201 NULL Highway, Hospital
Any direction would be greatly appreciated!
GROUP_CONCAT accepts a DISTINCT keyword which is useful in fan-out-queries. You can also use ORDER BY within a GROUP_CONCAT if you need your results to be ordred. See Documentation. Using this, we can write your intended query like below:
SELECT
c.id,
GROUP_CONCAT(DISTINCT ct.description) as 'Heating System',
GROUP_CONCAT(DISTINCT cst.description) as 'Nearby'
FROM characteristics c
LEFT JOIN characteristics_types ct ON ct.id = c.id
LEFT JOIN characteristics_sub_types cst
ON cst ON cst.code_a = c.code_a AND cst.code = c.code_b
GROUP BY 1
I would write separate subqueries, each one preforming the group_concat that you need, and join them together. Individually, I wrote them like this:
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id;
And the join like this:
SELECT c.id, t1.`Heating System`, t2.`Nearby`
FROM(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id) t1
JOIN(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Nearby'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'PROX'
GROUP BY c.id) t2 ON t1.id = t2.id;
Here is a working SQL Fiddle.
You can try this way:
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as descriptio_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as descriptio_nearby,
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as description_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as description_nearby
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id

left join logic not as expected

I have created a query that should, I believe, return all email addresses from table 1 regardless.
If I go SELECT COUNT(email), COUNT(DISTINCT email) contacts.sid208 I get 200,000 and 175000.
With this in mind, by using left joins the count of email from the following query result should be the same no?
SELECT
COUNT(email), COUNT(DISTINCT email)
FROM
(SELECT
co.email,
env.env_medium,
CAST(MIN(co.created) AS DATE) AS first_contact,
MIN(CASE
WHEN my.my_id = 581 THEN my.data
END) AS Created,
MIN(CASE
WHEN my.my_id = 3347 THEN my.data
END) AS Upgraded
FROM
contacts.sid208 co
LEFT JOIN contacts.my208 my ON co.id = my.eid
LEFT JOIN contacts.env208 env ON env.eid = co.id
WHERE
my_id = 581 OR my_id = 3347
GROUP BY email) b1
But the results here, if I keep things proportionate, are 150000 and 150000.
I expected the results to be 175000.
My understanding of LEFT JOIN was that all records from contacts.sid208 would be maintained, regardless of whether or not they appear in my208 or env208.
Is my understanding flawed here? Hope my query makes sense to folk, if there's any more info I can add to make my question clearer let me know.
For a left join, move the conditions to the join as well:
SELECT
COUNT(email), COUNT(DISTINCT email)
FROM
(SELECT
co.email,
env.env_medium,
CAST(MIN(co.created) AS DATE) AS first_contact,
MIN(CASE
WHEN my.my_id = 581 THEN my.data
END) AS Created,
MIN(CASE
WHEN my.my_id = 3347 THEN my.data
END) AS Upgraded
FROM
contacts.sid208 co
LEFT JOIN contacts.my208 my
ON co.id = my.eid
AND (my_id = 581 OR my_id = 3347)
LEFT JOIN contacts.env208 env ON env.eid = co.id
GROUP BY email) b1
If you don't do so, you will first perform the join, resulting in all rows from sid208, regardless, with null values for missing emails. But then the filtering in the where clause kicks in and those records are removed anyway.
When you move all those conditions to the join, you get all rows, and the emails are only joined when they have the matching contact id, and their own id is either 581 or 2247.

SQL SELECT DISTINCT newest line from group

I have a current query which returns all the values from the parameters queried.
However it returns older values when i only want the latest line for each to show, I have had a go at using max() and DISTINCT but i cannot get the output i want
Current Code
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE
(SELECT CASE
WHEN IME.TPL = 'V'
THEN 'WAIT'
ELSE 'WAIT'
END)
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN IME ON IUFV.G = IME.UFVG
Current Output
________________________________________________________________
|Time LID LNum Location Go To|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|2012-07-08 10:54:57.837 CCC CCC111 V WAIT |
|2012-07-08 12:15:07.000 CCC CCC111 Y GTG |
|2012-07-17 06:58:33.417 CCC CCC111 T WAIT |
|2012-08-09 03:51:20.750 BBB BBB222 Y GTG |
|2012-08-09 04:06:13.473 BBB BBB222 Y GTG |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Desired Output
________________________________________________________________
|Time LID LNum Location Go To|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|2012-07-17 06:58:33.417 CCC CCC111 T WAIT |
|2012-08-09 04:06:13.473 BBB BBB222 Y GTG |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE 'WAIT'
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN IME ON IUFV.G = IME.UFVG
INNER JOIN (Select Max(time) MTime, UFVG from IME
GROUP BY UFVG) B
on B.UFVG=IUFV.G
and B.MTime = IME.Time
Without a Deeper dive, I'm not sure if I could eliminate the additional join and just get the results from IME in the first place. but the need for TPL may necessitate the separate join
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE 'WAIT'
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN (Select Max(time) MTime, UFVG from IME
GROUP BY UFVG) B
on B.UFVG=IUFV.G
and B.MTime = IME.Time
Here's the basic approach. I'm assuming you never have duplicate timestamps within a group.
select *
from T
inner join (select GrpId, max(TimeStmp) from T group by GrpId) as MaxT
on MaxT.GrpId = T.GrpId and T.TimeStmp = MaxT.TimeStmp