divide column values (numbers) based on group by - php

First of all I am not really good with MySQL whatever experience I have I am putting it to make this query
In my query the main problem is with
left join subdealers as subdealer
ON
(
employees.Salesman1Number = subdealer.employee_number
OR employees.Salesman2Number = subdealer.employee_number
OR employees.Salesman3Number = subdealer.employee_number
)
I am trying to get the FrontGross, BackGross etc grouped by subdealer.group_name the problem is Saleman1Number & Salesman2Number might belong to same group_name and in the query below it counts them as two different Salesmen while what I want them to count as one in case the Salesman1Number, Saleman2Number and Salesman2Number belongs to same subdealer.group_name
For example: Salesman1Number belongs to group_name Fleet and Salesman2Number also belongs to Fleet
They both contributed to sell a single car. Now they both have half credit of what they sold and that credit goes to group_name Fleet as one, half from Salesman1Number and half from Salesman2Number
currently the query I wrote doesn't divide them in half depending on their group_name but count it as one from Salesman1Number
and one from Salesman2Number
SELECT count(core_leads.core_id) as leads,
count(new.id) as new,
count(used.id) as used,
IFNULL(SUM(profit.FrontGross) + SUM(finance.HoldbackAmount), 0) as FrontGross,
IFNULL(SUM(profit.BackGross) + SUM(profit.FinanceReserve), 0) as BackGross,
IFNULL(SUM(profit.TotalProfit), 0) as TotalProfit,
IFNULL(SUM(finance.HoldbackAmount), 0) as HoldbackAmount,
IFNULL(SUM(finance.Holdcheck), 0) as Holdcheck,
IFNULL(subdealer.group_name, 'Others') as group_name
from core_leads
inner join
(
select * from closed_deals
right join
(
select ContractDate, id as infoId, closed_deal_id
from closed_deal_infos
) as info
ON closed_deals.id = info.closed_deal_id
AND DATE(info.ContractDate) BETWEEN '2014-01-01' AND '2017-01-01'
) as closed
ON core_leads.core_id = closed.core_lead_id
AND core_leads.type != 'Unwind'
AND core_leads.type != 'Canceled'
left join closed_vehicles as used
ON closed.id = used.closed_deal_id
AND used.NewUsed = 'U'
left join closed_vehicles as new
ON closed.id = new.closed_deal_id
AND new.NewUsed = 'N'
left join closed_dealer_employees as employees
ON closed.id = employees.closed_deal_id
left join subdealers as subdealer
ON
(
employees.Salesman1Number = subdealer.employee_number
OR employees.Salesman2Number = subdealer.employee_number
OR employees.Salesman3Number = subdealer.employee_number
)
AND
(
subdealer.group_name = 'Fleet'
OR subdealer.group_name = 'Internet'
OR subdealer.group_name = 'Sales'
)
left join closed_profit as profit
ON closed.id = profit.closed_deal_id
left join closed_finance as finance
ON closed.id = finance.closed_deal_id
group by subdealer.group_name
This results this
While in the Fleet dept column name leads should be 38 instead of 40 because it is counting two different Salesmen whom belongs to same group_name as two
Let me know if I was not clear enough

To simplify your example i will use only two tables.
persons:
| personId | groupId |
|----------|---------|
| 1 | 1 |
| 2 | 2 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
| 6 | 5 |
activities:
| actId | person1Id | person2Id | person3Id | actValue |
|-------|-----------|-----------|-----------|----------|
| 1 | 1 | 2 | 3 | 1 |
| 2 | 1 | 2 | 4 | 10 |
| 3 | 5 | (null) | (null) | 100 |
A query which matches your problem would be:
select
p.groupId, count(a.actId) numActs, sum(a.actValue) sumVals, group_concat(a.actId) as acts
from activities a
left join persons p on (
a.person1Id = p.personId or
a.person2Id = p.personId or
a.person3Id = p.personId
)
group by p.groupId;
Result:
| groupId | numActs | sumVals | acts |
|---------|---------|---------|-------|
| 1 | 2 | 11 | 1,2 |
| 2 | 3 | 12 | 1,2,1 |
| 3 | 1 | 10 | 2 |
| 4 | 1 | 100 | 3 |
For the group with groupId=2 we have counted three activities (1,2,1). The Activity with actId=1 is counted twice because there are two persons from same group. To prevent that, we can define that a row for person2 should not be counted (should be filtered out) if person1 is from same group. And a row for person3 should not be counted if person1 or person 2 is from the same group. This can be done in the WHERE clause with dependent selects:
select
p.groupId, count(a.actId) numActs, sum(a.actValue) sumVals, group_concat(a.actId) as acts
from activities a
left join persons p on (
a.person1Id = p.personId or
a.person2Id = p.personId or
a.person3Id = p.personId
)
where (p.personId = a.person1Id
) or (
p.personId = a.person2Id and
p.groupId not in (select groupId from persons where personId = a.person1Id)
) or (
p.personId = a.person3Id and
p.groupId not in (select groupId from persons where personId in (a.person1Id, a.person2Id))
)
group by p.groupId;
Result:
| groupId | numActs | sumVals | acts |
|---------|---------|---------|------|
| 1 | 2 | 11 | 1,2 |
| 2 | 2 | 11 | 1,2 |
| 3 | 1 | 10 | 2 |
| 4 | 1 | 100 | 3 |
http://sqlfiddle.com/#!9/604a5/1
Note: If possible - you should consider to normalize your tables.

Related

MYSQL select recent record of each from

See my table(sample_table),
-----------------------------
id | from | to |
-----------------------------
1 | 2 | 1 |
3 | 2 | 1 |
4 | 2 | 4 |
5 | 3 | 2 |
9 | 3 | 1 |
11 | 4 | 1 |
12 | 4 | 3 |
-----------------------------
For each from, I would like the row holding the most recent to, where to = 1
I mean I want only following,
-----------------------------
id | from | to |
-----------------------------
3 | 2 | 1 |
9 | 3 | 1 |
11 | 4 | 1 |
-----------------------------
I Try following Query,
SELECT * FROM sample_table WHERE to = 1 GROUP BY from
It's giving first row of each. Help me.
Thanks,
There are many ways to do it and here is one way
select t1.* from sample_table t1
join(
select max(id) as id,`from` from
sample_table where `to` = 1
group by `from`
)t2
on t1.id= t2.id and t1.`from` = t2.`from`
https://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
Try this
select t1.id, t1.from, t1.to from table as t1 inner join
(
select to, from,min(id) as id from table
where to=1
group by to,from
) as t2
on t1.to=t2.to and t1.id=2.id and t1.from=t2.from

Complex NOT EXISTS MySQL query - To ignore multiple matches

Below is a snippet from a large query I am running which performs a search on a table full of products. This snippet excludes certain products from appearing in results unless it's overridden (a tag ID is specified eg.search criteria is made).
Here's a couple of tag ID's and their names:
Tag ID 576 = 'Santa'
Tag ID 123 = 'Christmas'
So basically the below will exclude all products in the 'tags_gifts_occasion_specific' table unless they have a tag ID of 576 ('Santa').
AND NOT EXISTS (
SELECT *
FROM tags_gifts_occasion_specific
WHERE tags_gifts_occasion_specific.tag_id != '576'
GROUP BY tags_gifts_occasion_specific.gift_id
)
Simple right....
But the problem is if the same product is in this table but has a different tag ID applied to it... eg it also is in there for the tag 'Christmas'.
Of course it would be easy to just add 'AND tags_gifts_occasion_specific.tag_id != '123', however the query doesn't know what additional ID's it shouldn't look for - only the one that has been searched for.
I have been playing around with something like the following but unsuccessfuly. I thought I could count the number of occurences of gift_id and if it was already found then ignore any further - any fresh input would be greatly appreciated.
AND NOT EXISTS (
SELECT * , COUNT( * ) AS no_gifts
FROM tags_gifts_occasion_specific
WHERE tags_gifts_occasion_specific.tag_id != '576'
HAVING no_gifts < 1
GROUP BY tags_gifts_occasion_specific.gift_id
)
** FURTHER DETAIL REGARDING ORDER BY BUG **
So the order by bug is as follows. If someone searches for 'wedding' & 'bridemaid' gifts, wedding or bridemaid gifts should be returned with matches for both showed first, then the rest based on popularity. This works for all searches that do not have gifts in the 'tags_gifts_occasion_specific' table.
SELECT gifts.affiliate_id, gifts.gift_id, gifts.gift_title, gifts.gift_price, gifts.gift_image, gifts.gift_slug
FROM gifts
LEFT JOIN tags_gifts_occasion_specific AS os ON gifts.gift_id = os.gift_id
LEFT JOIN tags_gifts ON tags_gifts.gift_id = gifts.gift_id
LEFT JOIN tags ON tags.id = tags_gifts.tag_id
LEFT JOIN popularity ON popularity.gift_id = gifts.gift_id AND popularity.tag_id = tags.id
WHERE published = '1' AND in_seekgifts = '1'
AND ( (tags_gifts.tag_id = '576' OR tags_gifts.tag_id = '340') )
AND NOT EXISTS (
SELECT * FROM tags_gifts_occasion_specific x WHERE x.gift_id = gifts.gift_id
AND NOT EXISTS(
SELECT * FROM tags_gifts_occasion_specific x1 WHERE x.gift_id = x1.gift_id AND ( tag_id IN (576) OR tag_id in (340) ) )
)
GROUP BY gifts.gift_id
ORDER BY COUNT(*) DESC , popularity.popularity DESC, gifts.gift_popularity DESC, gifts.gift_id DESC
Live example of results:
http://www.seekgifts.co.uk/wedding_bridesmaid/
What I think is happening is that it can see the products in the top are in the tags_gifts_occasion_specific table for multiple tags and is therefore ordering by the number of times it is found in this table. Not sure how to get round this one?
As far as I understand, there is a table with products, and there is a table with tags for each product.
And the query is expected to skip products that have some tag, and include products that don't have these tags.
For example:
Products
| PRODUCT_ID | PRODUCT_NAME |
|------------|--------------|
| 1 | Product 1 |
| 2 | Product 2 |
| 3 | Product 3 |
| 4 | Product 4 |
| 5 | Product 5 |
Tags
| PRODUCT_ID | TAG |
|------------|--------------|
| 1 | Santa |
| 1 | Christmas |
| 2 | Christmas |
| 3 | Santa |
| 4 | Christmas |
| 4 | Exclude |
| 4 | Dont exclude |
| 5 | Christmas |
| 5 | Dont exclude |
And we want to skip rpoducts, that have tags Santa and Exclude.
In the above example, we want to skip products 1, 3 and 4, and include products 2, 5.
We can do it using the following correlated subquery
SELECT * from products p
WHERE
NOT EXISTS(
SELECT 1 FROM tags t
WHERE t.product_id = p.product_id
AND t.tag IN ( 'Santa', 'Exclude' )
)
;
| PRODUCT_ID | PRODUCT_NAME |
|------------|--------------|
| 2 | Product 2 |
| 5 | Product 5 |
Here is a link to a working demo: http://sqlfiddle.com/#!2/d880d/4
EDIT
An example for your schema
Gifts
select * from `gifts`;
| GIFT_ID | GIFT_TITLE |
|---------|------------------------|
| 1 | Red Christmas stocking |
| 2 | Santa Clause Socks |
| 3 | 40th Birthday Mug |
| 4 | Red Bowl |
tags_gifts_occasion_specific
select * from `tags_gifts_occasion_specific`
order by gift_id, tag_id;
| TAG_ID | GIFT_ID |
|--------|---------|
| 3 | 2 |
| 4 | 2 |
| 4 | 3 |
| 3 | 4 |
This query gets all entries from tags_gifts_occasion_specific, except these ones for which exists a tag_id = 3
select * from `tags_gifts_occasion_specific` x
where not exists(
select 1 from `tags_gifts_occasion_specific` x1
where x.gift_id = x1.gift_id
and tag_id in ( 3 )
);
| TAG_ID | GIFT_ID |
|--------|---------|
| 4 | 3 |
Now we use the above query to exclude some records from gifts table:
select * from `gifts` g
where not exists(
select 1 from `tags_gifts_occasion_specific` x
where g.gift_id = x.gift_id
and not exists(
select 1 from `tags_gifts_occasion_specific` x1
where x.gift_id = x1.gift_id
and tag_id in ( 3 )
)
);
| GIFT_ID | GIFT_TITLE |
|---------|------------------------|
| 1 | Red Christmas stocking |
| 2 | Santa Clause Socks |
| 4 | Red Bowl |
| 5 | Red 40th Birthday Vase |
Here is a link to a demo: http://sqlfiddle.com/#!2/558422/5

Get customers that has more than 2 orders but logged in less than 3 times though SQL

I'm trying to get all customers who has more than 2 orders, but only logged in less than 3 times.
I'm logging when users are logging in.
But for some reason my returns only one row with wrong data...
"user_log" table (user_id 19 has logged in only once)
| user_log_id | date | user_id | type | module_id | unit_id |
|-------------|------|---------|------|-----------|---------|
| 1 |"date"| 19 | 1 | NULL | NULL |
| 2 |"date"| 20 | 1 | NULL | NULL |
| 3 |"date"| 20 | 1 | NULL | NULL |
| 4 |"date"| 20 | 1 | NULL | NULL |
| 5 |"date"| 20 | 1 | NULL | NULL |
|-------------|------|---------|------|-----------|---------|
"orders" table where user_id 19 has 2 orders (Removed unnecessary columns)
| order_id | user_id | status |
|----------|---------|--------|
| 10 | 19 | 1 |
| 11 | 19 | 1 |
| 12 | 20 | 1 |
| 13 | 21 | 1 |
| 14 | 31 | 1 |
|----------|---------|--------|
What i want (User_id has 2 orders, but has logged in less than 3 times)
| user_id |
|---------|
| 19 |
|---------|
This is how my SQL looks like right now.
$sql = "SELECT
ul.*, orders.order_id, orders.user_id, orders.firstname, orders.lastname, COUNT(ul.user_id) AS occourcence
FROM
orders
LEFT JOIN
user_log AS ul
ON
orders.user_id = ul.user_id
WHERE
orders.status = 1
AND
ul.type = 1
GROUP BY
orders.user_id
HAVING
COUNT(orders.user_id) > 1
ORDER BY
orders.order_id DESC";
select user_id,count(user_log_id) from user_log
where user_id in
(
select user_id from Orderes
group by user_id
having count(order_id) =2
)
group by user_id
having count(user_log_id) < 3
Avoiding any sub queries (and assuming you meant logged in 3 times, and making 2 or more orders - to match your example data):-
SELECT a.user_id
FROM user_log a
INNER JOIN orders b
ON a.user_id = b.user_id
GROUP BY a.user_id
HAVING COUNT(DISTINCT user_log_id) < 3 AND COUNT(DISTINCT order_id) >= 2;
SQL fiddle here:-
http://www.sqlfiddle.com/#!2/1b719/3
Try this
select o.user_id from
(
select user_id from orders
where status=1
group by user_id having count(*)>=2
) as o left join
(
select user_id from user_logs
where type=1
group by user_id having count(*)<3
) as l
on o.user_id=l.user_id

find top 3 values with an inner join

I have a table called users_preferred_zips that looks like this:
username | price | zip | program | active
-----------+---------+---------+-----------+---------
joe | 5 | 92108 | dog | 1
tom | 7 | 92108 | dog | 1
mary | 5 | 92108 | dog | 1
paul | 6 | 92108 | dog | 1
ron | 6 | 92108 | dog | 1
I have another table called users that looks like this
username | balance
-----------+----------
joe | 10
tom | 12
mary | 2
paul | 14
ron | 3
I need a query to pull AND sum the 3 highest values from the users_preferred_zips table where the username from the users table has a balance value greater than or equal to 5. I know i need to do some sort of inner join but my query below is not working. Here is the query i have:
SELECT SUM(price) AS SumOfTopValues
FROM (
SELECT users_preferred_zips . * , users.last_purchase, users.lesson_type, users.pref_acct_balance
INNER JOIN users ON ( users_preferred_zips.username = users.username )
WHERE users_preferred_zips.zip = '92108'
AND users_preferred_zips.program = 'dog'
AND users_preferred_zips.active = 1
AND users.pref_acct_balance >= '5'
ORDER BY price DESC
LIMIT 3
) AS sub
So the correct query would pull the following:
3 highest:
joe | 5
tom | 7
paul | 6
Sum of 3 highest values = 18
I feel like this should be pretty simple but i'm having a tough time! Thanks for your help
You can check this using:
SELECT SUM(price) AS SumOfTopValues
FROM users_preferred_zips
WHERE username IN (
SELECT username
FROM users
WHERE pref_acct_balance >= 5
)

Row count MySQL with three tables

My MySQL DB looks like this
**table_schools**
id | name
1 | school_1
2 | school_2
**table_classes**
id | class | school_id
1 | a | 1
2 | b | 1
3 | c | 2
4 | d | 2
5 | e | 2
**table_students**
id | name | class_id
1 | Nick | 1
2 | Tom | 2
3 | Kevin | 3
4 | Jane | 4
5 | Mark | 5
6 | Tim | 5
7 | Lynn | 5
I would like to have an output like this:
school_name | class_count | student_count
school_1 | 2 | 2
school_2 | 3 | 5
Is there a way to do this in ONE sql query? And how?
SELECT s.name, COUNT(DISTINCT c.id) AS classes, COUNT(st.id) AS students
FROM table_schools s
LEFT JOIN
table_classes c
ON c.school_id = s.id
LEFT JOIN
table_students st
ON st.class_id = c.id
GROUP BY
s.id
SELECT table_schools.name, COUNT(table_classes.name) AS classes, COUNT(table_students.id) AS students
FROM table_schools
LEFT JOIN table_classes ON table_schools.id = table_classes.school_id
LEFT JOIN table_students ON table_students.class_id = table_classes.id
GROUP BY table_schools.id, table_classes.id
ORDER BY table_schools.name

Categories