I've been staring at so much SQL code my brain is practically mush.
I'll make it quick. Below are my table structures, with irrelevant columns omitted:
attendance_history: member_info:
_____________________ ________________________
| Date | ID | | ID | Design Team |
|----------|--------| |--------|-------------|
| 1/27 | 1 | | 1 | DT1 |
| 1/28 | 1 | | 2 | DT2 |
| 1/29 | 2 | | 3 | DT2 |
| 1/29 | 3 | |--------|-------------|
|----------|--------|
My ultimate goal is to get an attendance average for every design team. But I believe I have all of the logic worked out except one part: I need to get the number of attendees for EACH design team for EACH date.
Here is an example of what I need:
__________________________________
| Date | Design Team | Count |
|----------|-------------|-------|
| 1/27 | DT1 | 1 |
| 1/28 | DT1 | 1 |
| 1/29 | DT1 | 0 |
| 1/27 | DT2 | 0 |
| 1/28 | DT2 | 0 |
| 1/29 | DT2 | 2 |
|----------|-------------|-------|
Any clues as to how I can achieve this? Currently I have a ridiculously long script that only gets the counts for the design teams if someone was present. I need somehow get a 0 for a count in a row if there was nobody present for that day from that team.
Thanks a ton, as always.
To get the zeros requires a bit of trickery. You need to generate all the rows first (using a cross join) and then left join in the attendance information:
select d.date, dt.design_team, coalesce(ddt.cnt, 0) as cnt
from (select distinct design_team from member_info) dt cross join
(select distinct date from attendance_history) d left outer join
(select ah.date, mi.design_team, count(*) as cnt
from attendance_history ah join
member_info mi
on ah.id = mi.id
group by ah.date, mi.design_team
) ddt
on ddt.design_team = dt.design_team and ddt.date = d.date;
Edit: Fixed subquery to join on ah.id = mi.id instead of ah.id = ah.id.
Related
I'm having a hard time setting up a query(select). Database is not my specialty, so I'm turning to the experts. Let me show what I need.
----companies--- ----company_server----- -----servers---- -----print------------------------
| id | name | | company | server | | id | name | | id |page|copy | date |server
|----|-------- | |---------|----------| |----|-------- | |----|----|-----|-------------
| 1 | Company1 |1--N| 1 | 1 |N*--1| 1 | Server1 |1--N| 1 | 2 | 3 | 2020-1-11 | 1
| 2 | Company2 | | 2 | 1 | | 2 | Server2 | | 2 | 1 | 6 | 2020-1-12 | 3
| 3 | Company3 | | 3 | 2 | | 3 | Server3 | | 3 | 4 | 5 | 2020-1-13 | 4
| 3 | 3 | | 4 | Server4 | | 4 | 5 | 3 | 2020-1-15 | 2
| 5 | 3 | 4 | 2020-1-15 | 4
| 6 | 1 | 2 | 2020-1-16 | 3
| 7 | 2 | 2 | 2020-1-16 | 4
What I need?
Example where date between CAST(2020-1-12 AS DATE) AND CAST(2020-1-15 AS DATE) group by servers.id
| companies | server | sum | percent
------------------------------------------------------------------------------------
| company1,company2 | server1 | sum(page*copy) = 0 or null | 0 or NULL
| company3 | server2 | sum(page*copy) = 15 | 28.30
| company3 | server3 | sum(page*copy) = 6 | 11.32
| NULL | server4 | sum(page*copy) = 32 | 60.38
Few notes:
I need this query for MYSQL;
Every Company is linked to at least one server.
I need result grouped by server. So, every company linked to that server must be concatenated by a comma.
If the company has not yet been registered, the value null should be presented.
The sum (page * copie) must be presented as zero or null (I don't care) in the case that there was no printing in the date range.
The percentage should be calculated according to the date range entered and not with all records in the database.
The field date is stored as MYSQL DATE.
Experts, I thank you in advance for your help. I currently solve this problem with at least 03 queries to the database, but I have a conviction that I could do it with just one query.
Added a fiddle. Sorry. Im still learing how to use this.
https://www.db-fiddle.com/f/dXej7QCPe9iDopfYd1SfVh/2
Follows the query that more or less represents how far I had arrived. Notice that in the middle of the way 'server4' disappeared because there are no values for it in print in the period searched for him and I am in possession of the total of the period but I cannot calculate the percentage.
i'm stuck
select
*
from
(select
sum(p.copy * p.page) as sum1,
s.name as s_name,
s.id as s_id
from
print p
join servers s on s.id = p.server
where p.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
group by s.id) as t1
join company_server cs on cs.server = t1.s_id
right join companies c on c.id = cs.company
cross join(
select
sum(p1.copy * p1.page) sum2
from
print p1
where p1.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
) as c;
I did this query before you add fiddle, so may be name of column of mine is not same as you. Anyway, this is my solution, hope it help you.
select group_concat(c.name separator ',') as name_company,
ss.name,
sum_print as sum,
(sum_print/total) *100 as percentage
from companies c
inner join company_server cs on c.id = cs.company
right join servers ss on ss.id = cs.id
left join
(
select server,sum(page*copy) as sum_print, date from print
where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)
group by server
) tmp on tmp.server = ss.id
cross join
(select sum(page*copy) as total from print where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)) tmp2
group by id
Group and concat by comma, using GROUP_CONCAT .
You can reference this image for JOIN clause.
https://i.stack.imgur.com/6cioZ.png
currently i have two tables with some data. the first table has the following:
+----------------+-----------+
| name | member_id |
+----------------+-----------+
| Juice Box | 49432 |
| Rainsurge | 49631 |
| spiderpigrider | 50482 |
+----------------+-----------+
The second table has the following:
+------------+-----------+
| recruit_id | bin(refs) |
+------------+-----------+
| 49432 | 1 |
| 49631 | 1 |
| 49432 | 1 |
| 49631 | 1 |
| 49432 | 1 |
| 49631 | 1 |
| 49432 | 1 |
| 49631 | 1 |
| 49432 | 1 |
| 49631 | 1 |
+------------+-----------+
I would like to return the name, total refs and member_id/recruit_id like so (listing only users with at least 1 ref)
+------------+-----------+------------+
| recruit_id | name | total_refs |
+------------+-----------+------------+
| 49631 | Rainsurge | 5 |
| 49432 | Juice Box | 5 |
+------------+-----------+------------+
select r.recruit_id,bin(r.refs),ipb.name from refs as r
inner join syndicate_ipb.core_members as ipb on ipb.member_id=r.recruit_id;
this returned my data but obviously without a total count and repeated names/ids
select r.recruit_id,count(bin(r.refs)),ipb.name from refs as r
inner join syndicate_ipb.core_members as ipb on ipb.member_id=r.recruit_id;
this returned data with the total count of everyone but only one id/name
+------------+--------------------+-----------+
| recruit_id | count(bin(r.refs)) | name |
+------------+--------------------+-----------+
| 49432 | 10 | Juice Box |
+------------+--------------------+-----------+
this returns the data but again without a count
select distinct r.recruit_id,bin(r.refs),ipb.name from refs as r
inner join syndicate_ipb.core_members as ipb on ipb.member_id=r.recruit_id;
+------------+-------------+-----------+
| recruit_id | bin(r.refs) | name |
+------------+-------------+-----------+
| 49432 | 1 | Juice Box |
| 49631 | 1 | Rainsurge |
+------------+-------------+-----------+
Any help or guidance is greatly appreciated. I feel like i'm close here but just not competent enough with SQL to get it. thanks!
You were almost there. You just missed the GROUP BY clause at the end.
Query:
SELECT
r.recruit_id,
count(bin(r.refs)),
ipb.name
FROM refs AS r
INNER JOIN syndicate_ipb.core_members AS ipb
ON ipb.member_id = r.recruit_id
GROUP BY r.recruit_id;
Note:
If bin(refs) column always contains value 1 then actually you don't need to keep that column. In that case you can use count(*) or count(r.recruit_id) to get the count.
And if bin(refs) column contains any value then count will not give you the right answer. In that case you need to use sum like Sum( bin(refs)).
You have to use the group by clause:
select r.recruit_id, ipb.name, count(bin(refs)) as total_refs
from refs as r
inner join syndicate_ipb.core_members as ipb
on ipb.member_id=r.recruit_id
group by r.recruit_id, ipb.name
having count(bin(refs)) >= 1
This group by r.recruit_id, ipb.name will group the results and this having count(bin(refs)) >= 1 will garante that it only returns members with at least one ref
Do not only group your columns just by the ones you want. Even though MySql allows it, it is not SQL Ansi pattern and even MySql now is complying with it. Use an aggregation function grouping with your entire columns on the select statement.
SELECT ipb.*, COUNT(`r`.`recruit_id`) AS cid FROM `ipb`
INNER JOIN `r` ON `r`.`join_id` = ipb.`member_id`
GROUP BY ipb.`member_id`
I need to retrieve rows from a mysql database as follows: I have a contract table, a contract line item table, and another table called udac. I need all contracts which DO NOT have a line item record with criteria based on a relationship between contract line item and udac. If there is a better way to state this question, let me know.
Table Structures
----contract--------------------- ---contractlineitem-----------
| id | customer_id | entry_date | | id | contract_id | udac_id |
--------------------------------- ------------------------------
| 1 | 1234 | 2010-01-01 | | 1 | 1 | 5 |
| 2 | 2345 | 2016-01-31 | | 2 | 1 | 2 |
--------------------------------- | 3 | 1 | 1 |
| 4 | 2 | 4 |
| 5 | 2 | 2 |
------------------------------
---udac----------
| id | udaccode |
-----------------
| 1 | SWBL/R |
| 2 | SWBL |
| 3 | ABL/R |
| 4 | ABL |
| 5 | XRS/F |
-----------------
Given the above data, contract 2 would show up but contract 1 would not, because it has contractlineitems that point to udacs that end in /F or /R.
Here's what i have so far, but it's not correct.
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND NOT EXISTS (
SELECT cli.id
FROM contractlineitem cli_i
JOIN udac u
ON cli_i.udac_id = u.id
WHERE u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R'
AND cli_i.contract_id = cli.contract_id);
Tom's comment that your WHERE clause is wrong may be the problem you are chasing. Plus, using a correlated subquery may be problematic for performance if the optimizer can't figure out a better way to do it.
Here is the better way to do it using an OUTER JOIN:
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
LEFT OUTER JOIN udac u
ON ( u.id = cli.udac_id
AND ( u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R' ) )
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND u.id IS NULL
Try that out and see if it does what you want. The query essentially does what you stated: It tries to join to udac where the code ends in '/F' or '/R', but then it only accepts the ones where it can't find a match (u.id IS NULL).
If the same row is returned multiple times incorrectly, throw a distinct on the front.
I am trying to join two tables and only select the dealers that did not have their promo code used on any order.
How can I do this?
I'm trying this below, but it's not working right. In the example I want to get just Bob, since his promo_code hasn't been used in any orders.
SELECT d.`name`
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
Here are my tables...
mysql> select * from z_dealer;
+----+------+------------+
| id | name | promo_code |
+----+------+------------+
| 1 | John | holiday |
| 2 | Suzy | special |
| 3 | Bob | laborday |
+----+------+------------+
mysql> Select * from z_order;
+----+-------+------------+
| id | total | promo_code |
+----+-------+------------+
| 1 | 10 | holiday |
| 2 | 20 | special |
| 3 | 15 | holiday |
| 4 | 45 | special |
+----+-------+------------+
SELECT d.`name` FROM z_dealer d LEFT JOIN z_order o ON (d.promo_code = o.promo_code) WHERE o.promo_code IS NULL
Have you tried INNER JOIN? or You can try IN like this :
SELECT d.name
FROM z_dealer d
WHERE d.promo_code not in( SELECT promo_code FROM z_order)
I'm not entirely sure why it's not working in your example code. I've created the same tables locally and when I run the script you provided I get the single 'Bob' answer.
SELECT d.name
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
What results are you seeing exactly?
Consider this database structure:
__________ __________
| Trucks | | Mileage |
|__________|__________ |__________|________________________
| ID | DRIVER | | TRUCK_ID | MILEAGE | OIL_CHANGE |
|---------------------| |-----------------------------------|
| 1 | Tony | | 1 | 100000 105000 |
| 2 | George | | 2 | 6020 10020 |
| 3 | Mary | | 3 | 37798 41000 |
|_____________________| | 3 | 41233 47200 |
| 3 | 49000 |
|___________________________________|
I want to end up with a result set containing the maximum miles and maximum oil_change for each driver.
_________________________________
| 1 | Tony | 100000 | 105000 |
| 2 | George| 6020 | 10020 |
| 3 | Mary | 49000 | 47200 |
|_______________________________|
This is what I have tried so far:
SELECT t.*, MAX(m.mileage) AS mileage, MAX(m.oil_change) AS oil_change
FROM trucks t
LEFT JOIN mileage m ON t.id = m.truck_id
GROUP BY t.id
But this doesn't seem to allow the MAX function to work properly. It does not always contain the actual maximum value for mileage
Got it! Your mileage column must be defined as a character type, not a numeric type! When than happens, order is done alphabetically, not by value.
You should convert your mileage and oil_change columns to a numeric type (I'd recommend INT based on the data sample provided).
While you don't convert them, this will work:
SELECT t.*, MAX(cast(m.mileage as int)) AS mileage,
MAX(cast(m.oil_change as int)) AS oil_change
FROM trucks t
LEFT JOIN mileage m ON t.id = m.truck_id
GROUP BY t.id
The below queries should work for your question.
SELECT T.DRIVER,MIN(MILEAGE) AS MIN_MILEAGE,MIN(OIL_CHANGE) AS MIN_OIL_CHANGE
FROM TRUCKS T INNER JOIN MILEAGE M
ON T.ID = M.TRUCK_ID
GROUP BY T.DRIVER;
SELECT T.DRIVER,MAX(MILEAGE) AS MAX_MILEAGE,MAX(OIL_CHANGE) AS MAX_OIL_CHANGE
FROM TRUCKS T INNER JOIN MILEAGE M
ON T.ID = M.TRUCK_ID
GROUP BY T.DRIVER;
Regards
Venk