I'm trying to query analytical data from a MySQL 5.7 database for displaying in the frontend. This data is in a many to many structure, and I want to aggregate two columns, one based on the information in the previous column, grouped and counted by date.
The intention is to get data in the following format (see the example data):
entry_date | cat_name | subcat_name | subcat_count | total_count
-----------|----------|-------------|--------------|------
2020-07-28 | #TestOne | Alpha | 1 | 2
2020-07-28 | #TestOne | Delta | 1 | 2
2020-07-27 | #TestTwo | Bravo | 1 | 2
2020-07-27 | #TestTwo | Charlie | 1 | 2
2020-07-26 | #TestOne | Charlie | 1 | 2
2020-07-26 | #TestOne | Bravo | 1 | 2
2020-07-25 | #TestTwo | Delta | 1 | 2
2020-07-25 | #TestTwo | Alpha | 1 | 2
In the above data, value is the quantity of a given type against a given scenario on a given date, and total is the quantity of all types against a scenario on a given day. So if there were a hundred posts with type Alpha on one day, that value would be 100 and the total would be 100. If there were another hundred posts with type Delta, the total would become 200.
I got as far as this before I realised I was lost:
SELECT
ct.entry_id,
DATE(FROM_UNIXTIME(ct.entry_date)) AS entry_date,
cg.group_name,
c.cat_name
FROM
category_posts cp
LEFT JOIN channel_titles ct ON ct.entry_id = cp.entry_id
LEFT JOIN categories c ON c.cat_id = cp.cat_id
LEFT JOIN category_groups cg ON cg.group_id = c.group_id
WHERE
cg.group_name = 'Group A'
OR cg.group_name = 'Group B'
GROUP BY
entry_date,
group_name,
cat_name
ORDER BY
entry_id,
FIELD(group_name, 'Group A', 'Group B')
This returned data in an unreliable and less useful format, but was okay – if you could be absolutely certain of the presence of records in Group A, which I couldn't be.
entry_id | entry_date | group_name | cat_name
---------|------------|------------|---------
1 | 2020-07-28 | Group A | #TestOne
1 | 2020-07-28 | Group B | Alpha
1 | 2020-07-28 | Group B | Delta
2 | 2020-07-27 | Group A | #TestTwo
2 | 2020-07-27 | Group B | Bravo
2 | 2020-07-27 | Group B | Charlie
3 | 2020-07-26 | Group A | #TestOne
3 | 2020-07-26 | Group B | Charlie
3 | 2020-07-26 | Group B | Bravo
4 | 2020-07-25 | Group A | #TestTwo
4 | 2020-07-25 | Group B | Delta
4 | 2020-07-25 | Group B | Alpha
Entity Relationship Diagram
Example Data
category_groups
group_id | group_name
---------|------------------------------
1 | Group A
2 | Group B
categories
cat_id | group_id | cat_name
-------|----------|-------------------
1 | 1 | #TestOne
2 | 1 | #TestTwo
3 | 2 | Alpha
4 | 2 | Bravo
5 | 2 | Charlie
6 | 2 | Delta
category_posts
cat_id | entry_id
-------|---------
1 | 1
2 | 2
1 | 3
2 | 4
3 | 1
4 | 2
5 | 3
6 | 4
6 | 1
5 | 2
4 | 3
3 | 4
channel_titles
entry_id | entry_date
---------|-----------
1 | 1595940540 (07/28/2020)
2 | 1595882160 (07/27/2020)
3 | 1595721600 (07/26/2020)
4 | 1595635200 (07/25/2020)
The original version of the question did not specify MySQL 5.7. This answer requires MySQL 8.0.
But I think this is just an aggregation query with a window function:
SELECT ct.entry_id,
DATE(FROM_UNIXTIME(ct.entry_date)) AS entry_date,
cg.group_name,
COUNT(*) as value,
SUM(COUNT(*)) OVER (PARTITION BY entry_id, DATE(FROM_UNIXTIME(ct.entry_date)) as total
FROM category_posts cp JOIN
channel_titles ct
ON ct.entry_id = cp.entry_id JOIN
categories c
ON c.cat_id = cp.cat_id JOIN
category_groups cg
ON cg.group_id = c.group_id
WHERE cg.group_name IN ( 'Group A', 'Group B' )
GROUP BY entry_date, group_name;
I removed the LEFT JOINs because your data all seems to match and your results have no NULL values in the key columns.
Related
I have a working query using INNER JOIN and a subquery but was wondering if there is a more effient way of writing it.
with prl
as
(
SELECT `number`, creator, notes
FROM ratings
INNER JOIN
projects on ratings.project_id = projects.project_id
WHERE ratings.rating = 5 AND projects.active = 1
)
SELECT prl.`number`, creator, notes
FROM prl
INNER JOIN(
SELECT `number`
HAVING COUNT(creator) > 1
)temp ON prl.`number` = temp.`number`
ORDER BY temp.`number`
projects table
project_id| number | creator | active |
| 1 | 3 | bob | 1 |
| 2 | 4 | mary | 1 |
| 3 | 5 | asi | 1 |
rating table
project_id| notes | rating |
| 1 | note1 | 5 |
| 1 | note2 | 5 |
| 3 | note3 | 5 |
| 1 | note4 | 1 |
| 2 | note5 | 5 |
| 3 | note6 | 2 |
result
| number | creator | notes |
| 3 | bob | note1 |
| 3 | bob | note2 |
It seems like you're using MySQL version that support window function. If so, then try this:
SELECT number, creator, notes
FROM
(SELECT p.number, p.creator, r.notes,
COUNT(creator) OVER (PARTITION BY creator) AS cnt
FROM project p
JOIN rating r ON p.project_id=r.project_id
WHERE r.rating=5
AND p.active = 1) v
WHERE cnt=2;
As far as whether this is more efficient, I'm not really sure because it depends in your table indexes but for a small dataset, I assume this will do well.
Demo fiddle
I have the following tables
table anag (customer registry)
id | surname | name | phone |
----------------------------------------------
1 | Brown | Jack | +3989265781 |
2 | Smith | Bill | +3954872358 |
3 | Rogers | Stan | +3912568453 |
4 | Pickford | Eric | +3948521358 |
----------------------------------------------
table levels (table that connects each customer to his salesperson. For database registration reasons, the link between customer and seller is given by the customer's telephone number)
id | client_phone | id_seller |
--------------------------------------
1 | +3989265781 | 4 |
2 | +3954872358 | 7 |
3 | +3912568453 | 7 |
4 | +3948521358 | 8 |
--------------------------------------
table orders (contains all purchases made by customers, of course)
id | id_client | id_item | id_seller | price | status |
--------------------------------------------------------------------
1 | 1 | 2 | 4 | 12.50 | 2 |
2 | 2 | 2 | 7 | 12.50 | 2 |
3 | 2 | 3 | 7 | 10.00 | 3 |
4 | 2 | 3 | 7 | 10.00 | 3 |
5 | 2 | 4 | 7 | 20.50 | 1 |
6 | 3 | 2 | 7 | 12.50 | 1 |
7 | 3 | 5 | 7 | 19.00 | 3 |
8 | 3 | 7 | 7 | 31.00 | 2 |
9 | 4 | 1 | 8 | 5.00 | 1 |
--------------------------------------------------------------------
What I'm trying to do is get from the JOIN of these tables a complete list by seller of his customers sorted in descending order by the amount spent on orders as long as the order status is 2 or 3
Something like this (example seller id 7):
id | surname | name | amaount |
----------------------------------------
3 | Rogers | Stan | 50.00 |
2 | Smith | Bill | 32.50 |
----------------------------------------
I have tried with this query which seems correct to me, but unfortunately it returns me error in fetch_assoc()
SELECT a.id, a.surname, a.name, o.amount FROM levels AS l
JOIN anag AS a ON a.phone = l.client_phone
JOIN {
SELECT id_client, SUM(price) AS amount FROM orders
WHERE id_seller = '7' AND (status = '2' OR status = '3') GROUP BY id_client
} AS o ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC
If I separate the subquery from the main query, both return the data I expect and it seems strange to me the JOIN between the two does not work properly
I think the only real error is using curly braces instead of parentheses:
SELECT a.id, a.surname, a.name, o.amount
FROM levels l JOIN
anag a
ON a.phone = l.client_phone JOIN
(SELECT id_client, SUM(price) AS amount
FROM orders
WHERE id_seller = '7' AND status IN ('2', '3'))
GROUP BY id_client
) o
ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC;
In addition:
You can use IN to shorten an equality comparison to multiple values.
Although I left them in, status and id_seller look like numbers. If so, drop the single quotes. Don't mix data types.
Your question is ambiguous on what to do if the seller in orders differs from the seller in anag for a customer. This keeps your logic (the sellers need to match).
SELECT a.id, a.surname, a.name, sum(o.price) 'amount'
FROM anag a
LEFT JOIN levels l ON l.id =a.id
LEFT JOIN orders of ON o.id_seller = l.id_seller AND o.id_client = l.id
GROUP BY o.id_seller
ORDER BY amount DESC
I have 3 MySQL tables. I've trimmed them down to show just the important columns
items
id | title |
1 | Hamlet |
2 | Romeo and Juliet |
3 | The Merchant of Venice |
listings
id | item_id | condition |
1 | 1 | Mint |
2 | 1 | Used - Good |
3 | 2 | New |
4 | 2 | Mint |
5 | 2 | Used - Poor |
6 | 3 | Used - Poor |
7 | 3 | Used - Good |
8 | 3 | Used - Good |
itemListings_variations
id | listing_id | price
1 | 1 | 20.00
2 | 1 | 10.00
3 | 1 | 5.00
4 | 2 | 6.00
5 | 2 | 5.50
6 | 3 | 2.00
7 | 4 | 12.00
8 | 5 | 3.00
9 | 6 | 9.00
If e.g. I search for Romeo and Juliet, it should return:
item_id | title | min_price
2 | Romeo and Juliet | 2.00
So far I have two queries which somehow need combining via subqueries and joins. I have:
SELECT
i.id AS item_id,
i.title
FROM items i
WHERE MATCH (i.title) AGAINST (:search)
also:
SELECT
l.item_id,
v.listing_id,
MIN(price) AS min_price
FROM itemListings_variation v, itemListings l
WHERE v.listing_id = l.id
GROUP BY v.listing_id
Try this -
SELECT i.id, i.title, MIN(price)
FROM items i
INNER JOIN listings l ON i.items = l.item_id
INNER JOIN itemListings_variations il ON l.id = il.listing_id
GROUP BY i.id, i.title
Hope this helps.
No group by is needed as calling min(price) will return the row with the lowest price.
select t1.id, title, min(price) from
(select * from items where title = 'Romeo and Juliet' ) as t1
left join listings on t1.id = listings.item_id
left join itemlistings_variations on listing_id = listings.id;
Tested on my test server.
How can i achieve this in mysql?
Result table
+-----------------+-----------------------+------+
|st-id | term | score|
+-----------------+-----------------------+------+
| 20001 | 1 | 5 |
| 20002 | 1 | 6 |
| 20001 | 2 | 6 |
| 20002 | 2 | 4 |
| 20003 | 1 | 7 |
| 20003 | 2 | 9 |
+-----------------+-----------------------+------+--
Result query should be like this
+--------------------------------+
|st-id | score->term 1 | score->term 2
+-------------------------------+
| 20001 | 5 | 6 |
| 20002 | 6 | 4 |
| 20003 | 7 | 9 |
+-------------------------------+
Tried
(select st-id from result-table where term=1) union (select st-id from result-table where term=2)
but it appends the result.
you should use a join (left if the rows don't always match inner if the always match)
select a.st-id, a.score as `score->term 1`, b.score as `score->term 2`
from `result-table` as a
left join `result-table` as a on a.`st-id` = b.`st-id`
where a.term=1
and b.term=2
or
select a.`st-id`, a.score as `score->term 1`, b.score as `score->term 2`
from `result-table` as a
inner join `result-table a`s a on a.`st-id` = b.`st-id`
where a.term=1
and b.term=2
and you should not use name as st-id (the - is an operatore in mysql) .. if you really need use backtics
I have 3 tables:
applications (has many votes)
votes (belongs to applications and questions)
questions (has many votes)
I need to get number of votes per application per question.
So, my attempt was:
SELECT applications.id, COUNT(votes.id), votes.question_id
FROM applications
LEFT OUTER JOIN votes ON (votes.application_id = application.id)
GROUP BY votes.question_id
However, it displays data only for a single application, so I assume my query is malformed:
+----+-----------------+-------------+
| id | COUNT(votes.id) | question_id |
+----+-----------------+-------------+
| 1 | 1185 | 1 |
| 1 | 1170 | 2 |
| 1 | 1209 | 3 |
| 1 | 1230 | 4 |
| 1 | 1213 | 5 |
+----+-----------------+-------------+
What I need:
+----+-----------------+-------------+
| id | COUNT(votes.id) | question_id |
+----+-----------------+-------------+
| 1 | 1185 | 1 |
| 1 | 1170 | 2 |
| 1 | 1209 | 3 |
| 1 | 1230 | 4 |
| 1 | 1213 | 5 |
| 2 | null | 1 |
| 2 | 50 | 2 |
| 2 | 333 | 3 |
| 2 | 1230 | 4 |
| 2 | 1213 | 5 |
| 3 | null | 1 |
| 3 | 50 | 2 |
| 3 | 333 | 3 |
| 3 | null | 4 |
| 3 | 5555 | 5 |
+----+-----------------+-------------+
The group by clause was missing applications.id.
SELECT applications.id, COUNT(votes.id), votes.question_id
FROM applications
LEFT OUTER JOIN votes ON votes.application_id = application.id
group by applications.id, votes.question_id
You should be grouping by the applications.id as well as the questions.id:
SELECT a.id, COUNT(votes.id), votes.question_id
FROM applications a LEFT OUTER JOIN
votes v
ON v.application_id = a.id
GROUP BY a.id, v.question_id;
However, this will not produce exactly what you want. You seem to want all the questions for the applications, regardless of whether or not there are any votes. If so, this is probably what you want:
SELECT a.id, q.question_id, COUNT(v.application_id)
FROM applications a CROSS JOIN
(SELECT DISTINCT question_id FROM votes) q LEFT JOIN
votes v
ON v.application_id = a.id and v.question_id = q.question_id
GROPU BY a.id, q.question_id;