MySQL query to find missing values in time series - mysql

I have a table containing thousands of customer records, with customer ids ideally repeating for every month.
Customer Org | CustomerID | Month
001 | 111 | Jan-15
001 | 112 | Jan-15
001 | 113 | Jan-15
001 | 111 | Feb-15
001 | 112 | Feb-15
001 | 111 | Mar-15
001 | 113 | Mar-15
I would like to write a SQL query that gives me 'inconsistent' customers within a customer org, i.e those who do not have records every month.
For eg, if I pass the customer org '001', the expected output needs to be
Jan-15 | Feb-15 | Mar-15
112 | 112 | -
113 | - | 113
Thanks for your help in advance

Related

How to get the opposite of a join?

I am trying to get the rows that don't exist in one table where one table called schedules (match_week, player_home_id, player_away_id) and the other table called match (match_week, Winner_id, Defeated_id) are joined. The players look at their schedule and play a match. I am trying to get a list of the scheduled matches that do not exist in the match table. The IDs in the match table can be in either column Winner_id or Defeated_id.
I have reviewed a number of Stack Exchange examples, but most use "IS NULL" and I don't have null values. I have used a Join that does give the output of the matches played. I would like the matches that have not been played.
CSV - wp_schedule_test
+----+------------+--------------+--------------+-----------------+-----------------+
| ID | match_week | home_player1 | away_player1 | player1_home_id | player1_away_id |
+----+------------+--------------+--------------+-----------------+-----------------+
| 1 | WEEK 1 | James Rives | Dale Hemme | 164 | 169 |
| 2 | WEEK 1 | John Head | David Foster | 81 | 175 |
| 3 | WEEK 1 | John Dalton | Eric Simmons | 82 | 23 |
| 4 | WEEK 2 | John Head | James Rives | 81 | 164 |
| 5 | WEEK 2 | Dale Hemme | John Dalton | 169 | 82 |
| 6 | WEEK 2 | David Foster | Eric Simmons | 175 | 23 |
| 7 | WEEK 3 | John Dalton | James Rives | 82 | 164 |
| 8 | WEEK 3 | John Head | Eric Simmons | 81 | 23 |
| 9 | WEEK 3 | Dale Hemme | David Foster | 169 | 175 |
| 10 | WEEK 4 | Eric Simmons | James Rives | 23 | 164 |
| 11 | WEEK 4 | David Foster | John Dalton | 175 | 82 |
| 12 | WEEK 4 | Dale Hemme | John Head | 169 | 81 |
+----+------------+--------------+--------------+-----------------+-----------------+
CSV - wp_match_scores_test
+----+------------+------------+------------+
| ID | match_week | player1_id | player2_id |
+----+------------+------------+------------+
| 5 | WEEK 1 | 82 | 23 |
| 20 | WEEK 1 | 164 | 169 |
| 21 | WEEK 2 | 164 | 81 |
| 25 | WEEK 2 | 82 | 169 |
| 61 | WEEK 3 | 175 | 169 |
| 62 | WEEK 4 | 175 | 82 |
| 69 | WEEK 2 | 175 | 23 |
| 85 | WEEK 3 | 164 | 82 |
| 86 | WEEK 4 | 164 | 23 |
+----+------------+------------+------------+
The output from the mysql query are the matches that have been played. I am trying to figure out how to list the matches that have not been played from the table Schedule.
CSV - MySQL Output
+------------+------------+------------+
| match_week | player1_id | player2_id |
+------------+------------+------------+
| WEEK 1 | 164 | 169 |
| WEEK 1 | 82 | 23 |
| WEEK 2 | 164 | 81 |
| WEEK 2 | 82 | 169 |
| WEEK 2 | 175 | 23 |
| WEEK 3 | 175 | 169 |
| WEEK 3 | 164 | 82 |
| WEEK 4 | 175 | 82 |
| WEEK 4 | 164 | 23 |
+------------+------------+------------+
MYSQL
select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week
The expected output is:
CSV - Desired Output
+------------+----------------+----------------+
| match_week | player_home_id | player_away_id |
+------------+----------------+----------------+
| WEEK 1 | 81 | 175 |
| WEEK 3 | 81 | 23 |
| WEEK 4 | 169 | 81 |
+------------+----------------+----------------+
The added code I would like to use is
SELECT s.*
FROM wp_schedules_test s
WHERE NOT EXISTS
(select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week)
Unfortunately, the output yields "No Rows"
You can use a LEFT JOIN to achieve the desired results, joining the two tables on matching player ids (noting that player id values in wp_match_scores_test can correspond to either player1_home_id or player1_away_id in wp_schedules_test). If there is no match, the result table will have NULL values from the wp_match_scores_test table values, and you can use that to select the matches which have not been played:
SELECT sch.*
FROM wp_schedule_test sch
LEFT JOIN wp_match_scores_test ms
ON (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id)
WHERE ms.ID IS NULL
Output:
ID match_week home_player1 away_player1 player1_home_id player1_away_id
2 Week 1 John Head David Foster 81 175
8 Week 3 John Head Eric Simmons 81 23
12 Week 4 Dale Hemme John Head 169 81
Note that you can also use a NOT EXISTS query, using the same condition as I used in the JOIN:
SELECT sch.*
FROM wp_schedule_test sch
WHERE NOT EXISTS (SELECT *
FROM wp_match_scores_test ms
WHERE (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id))
The output of this query is the same. Note though that conditions in the WHERE clause have to be evaluated for every row in the result set and that will generally make this query less efficient than the LEFT JOIN equivalent.
Demo on dbfiddle

Group last rows in a single one summing values

I have this mysql table:
date | client | total
2015-01-01 | john | 85.00
2015-01-01 | alfred | 35.00
2015-01-01 | georgy | 125.00
2015-01-01 | linda | 42.00
2015-01-01 | alex | 76.00
2015-01-01 | john | 94.00
2015-01-01 | john | 75.30
I wanted to group by client name calculating total sum amount and percent, so I made this query:
SELECT client, total, round(total*100/t.th,1) as percent
FROM (
SELECT client, sum(total) as total
FROM mytable
WHERE date='2015-01-01'
GROUP BY `client`) c
JOIN ( select sum(total) as th from mytable
WHERE date='2015-01-01') t
order by percent desc
It works great obtaining this result:
client | total | percent
john | 254.3 | 47.8
georgy | 125 | 23.5
alex | 76 | 14.3
linda | 42 | 7.9
alfred | 35 | 6.6
But I don't know if it's possible to sum last rows and group in a single one obtaining a result like this, for example:
client | total | percent
john | 254.3 | 47.8
georgy | 125 | 23.5
others | 135 | 28.8
I would like to know how can I modify my query to obtain this result.
I'd like some help.

how to query sum for one field of two mysql tables

I have two mysql table.
Table: bill
id | billtype | amount | advid | paydate |adjid | adjdate |
1 | electric | 10000 | 123 | 2017-01-01 | 50 | 2017-01-03 |
2 | Water | 5000 | 124 | 2017-02-01 | 0 | 0000-00-00 |
3 | Shirt | 500 | 125 | 2017-03-01 | 0 | 0000-00-00 |
Table: advance
id | advid | amount | balance | purpose |
1 | 123 | 50000 | 20000 | Bill |
2 | 124 | 70000 | 10000 | Bill |
3 | 125 | 55000 | 15000 | Uniform |
4 | 124 | 60000 | 10000 | Bill |
I want to create a drop down menu so that to select those 'advance' which are not adjusted yet (adjid=0 and adjdate=0000-00-00) in Table: bill and that drop down menu will also contain the total value of advance for same advance id (advid) like below:
<option>Bill-130000</option>
<option>Uniform-55000</option>
As total 130000 (70000+60000) advance is taken against advance id 124, so the Total amount of Advance in Option menu should be 130000 in case of Bill. But I am failed to calculate total amount of advance accurately:
SELECT sum(a.amount), purpose FROM bill as b, advance as a WHERE b.paydate!='0000-00-00' AND b.adjid!=0 AND a.advid=b.advid GROUP BY a.advid
Total amount in <option></option> is not coming actual.
What would be the right query for this purpose?
You could try
SELECT SUM(a.amount) AS amount,
MAX(purpose) AS purpose
FROM advance a
WHERE a.advid IN (
SELECT b.advid
FROM bill b
WHERE b.paydate = '0000-00-00'
AND b.adjid = 0)
GROUP BY a.advid

Output of order numbers and Revenue where all PO numbers tied to the order number has Shipped or is ready to Ship

I am new to SQL and trying to get an output of order numbers and Revenue where all PO numbers tied to the order number has Shipped or is ready to Ship. My table is called Orders and looks like this:
PO_NUM | ORDER_NUM | STATUS | REVENUE
101 | 001 | In Production | 1.00
102 | 001 | Shipped | 1.00
103 | 001 | Shipped | 1.00
104 | 001 | Ready to Ship | 1.00
201 | 002 | In Production | 1.00
202 | 002 | In Production | 1.00
203 | 002 | In Production | 1.00
301 | 003 | Ready to Ship | 1.00
401 | 004 | Shipped | 1.00
402 | 004 | Shipped | 1.00
403 | 004 | Shipped | 1.00
501 | 005 | Ready to Ship | 1.00
502 | 005 | Shipped | 1.00
503 | 005 | Shipped | 1.00
Based on this table output would look like this:
ORDER_NUM | REVENUE
003 | 1.00
004 | 3.00
005 | 3.00
I am sure this is probably simple but I just can’t figure it out. Please help.
EDIT: I only want to return order numbers if status of all PO numbers tied to the Order number has shipped or is ready to be shipped.
There are many ways of doing this, but here is one:
SELECT ORDER_NUM, REVENUE
FROM Orders
WHERE PO_NUM IN(
SELECT TOTALORDERS.PO_NUM
FROM
(SELECT PO_NUM, count(*) as TOTAL
FROM Orders
GROUP BY PO_NUM) as TOTALORDERS
INNER JOIN
(SELECT PO_NUM, count(*) as PROCESSED
FROM Orders
WHERE [STATUS] IN('Shipped','Ready to Ship')
GROUP BY PO_NUM) as PROCESSEDORDERS
ON TOTALORDERS.PO_NUM=PROCESSEDORDERS.PO_NUM
AND TOTALORDERS.TOTAL=PROCESSEDORDERS.PROCESSED)

Group column values together in one cell

I need to write an SQL select statement that groups together values from one column into one cell.
e.g.
table name: Customer_Hobbies
+------------+------------+-----------+
| CustomerId | Age | Hobby |
+------------+------------+-----------+
| 123 | 17 | Golf |
| 123 | 17 | Football |
| 324 | 14 | Rugby |
| 627 | 28 | Football |
+------------+------------+-----------+
should return...
+------------+------------+----------------+
| CustomerId | Age | Hobbies |
+------------+------------+----------------+
| 123 | 17 | Golf,Football |
| 324 | 14 | Rugby |
| 627 | 28 | Football |
+------------+------------+----------------+
Is this possible?
N.B. I know the data's not laid out in a particularly sensible way, but I can't change that.
You want group_concat():
select customerId, age, group_concat(hobby) as hobbies
from t
group by customerId, age