MySql multiple results tables for each unique values - mysql

Sorry if the title is not well explained tried my best.
I currently have this transactions table which hold the records, every row has an agent and a currency assigned to it.
id
amount
agent
currency_id
1
400.00
agent1
1
2
170.00
agent5
3
3
110.00
agent4
2
4
430.00
agent5
3
5
155.00
agent1
1
6
370.00
agent2
2
7
10.00
agent2
2
8
150.00
agent1
1
9
130.00
agent3
3
10
445.00
agent4
2
And this other table called currency which holds the unique currency and name.
id
currency
1
USD
2
VES
3
EUR
The query that I want to make is a SUM and group by agent for every currency there is. I am able to do it with a single query like this but only for one currency in the WHERE clause:
SELECT a.agent,
SUM(a.amount)
FROM transactions AS a
INNER JOIN currency AS b ON b.id = a.currency_id
WHERE b.currency = 'VES'
GROUP BY a.agent
I will be getting this result which is only for the VES currency
agent
total
agent2
380.00
agent4
555.00
I am looking for one query that allow me to get the result of all 3 current existing currencies (USD, VES, EUR) this should give a result of 3 different tables

I suspect that you want a report showing all agents, currencies, and their sums. You may try using this cross join approach:
SELECT a.agent, c.currency, COALESCE(SUM(t.amount), 0) AS total
FROM (SELECT DISTINCT agent FROM transactions) a
CROSS JOIN currency c
LEFT JOIN transactions t
ON t.agent = a.agent AND
t.currency_id = c.id
GROUP BY 1, 2
ORDER BY 1, 2;
The first two tables in the join generate all combinations of agents and currencies. We join this to your transactions table and aggregate to get the totals.

Related

Select from two tables mysql in a particular order because the output must have a particular structure

I have a unique situation where I have to select particular columns from two tables in a particular order. Below is the table for the vendors
vid
company
mno
phone
1
Xoxoxo
MTN
2459999999
2
Vovovo
MTN
2459998888
3
Yrewmi
Vodafone
2459997777
Here is the order_content table.
oid
vendor_id
amount
2
2
2
2
1
4
2
1
7
3
1
1
I want to select the vendor.company, vendor.mno, SUM(order_content.amount), vendor.phone for a particular ordercontent.oid selecting only vendors involved. I want to get the vendors details from the vendor table and sum up the amount for a particular order. Lets say order 2 that is old = 2.
SELECT DISTINCT vendor.company AS customerName, vendor.mno, SUM(orders_content.price) AS amount
FROM orders_content, vendor
WHERE orders_content.oid = 2
GROUP BY orders_content.oid;
The result I am expecting
customerName
mno
amount
phone
description
reference
Vooxoo
MTN
2
2459998888
orders
154013949
Xoxoxo
MTN
11
2459999999
orders
78793949
You need to specify a joining condition between the tables, and group by vid, not oid. You don't need to group by oid since the WHERE clause restricts to a single order.
SELECT vendor.company AS customerName, vendor.mno, SUM(orders_content.price) AS amount
FROM vendor
JOIN orders_content ON orders_content.vendor_id = vendor.vid
WHERE orders_content.oid = 2
GROUP BY vendor.vid

Trying to get latest status for related shipment but the results I receive are incorrect

I am currently working on a project while trying to learn MySQL and I would like to join three tables and get the latest status for each related shipment. Here are the tables I'm working with (with example data):
shipments
id
consignee
tracking_number
shipper
weight
import_no
1
JOHN BROWN
TBA99900000121
AMAZON
1
101
2
HELEN SMITH
TBA99900000190
AMAZON
1
102
3
JACK BLACK
TBA99900000123
AMAZON
1
103
4
JOE BROWM
TBA99900000812
AMAZON
1
104
5
JULIA KERR
TBA99900000904
AMAZON
1
105
statuses
id
name
slug
1
At Warehouse
at_warehouse
2
Ready For Pickup
ready_for_pickup
3
Delivered
delivered
shipment_status (pivot table)
id
shipment_id
status_id
1
1
1
2
2
1
3
3
1
4
4
1
5
5
1
6
1
2
7
2
2
8
3
2
9
4
2
10
5
2
all tables do have created_at and updated_at timestamp columns
Example of the results I'm trying to achieve
slug
shipment_id
status_id
ready_for_pickup
1
2
ready_for_pickup
2
2
ready_for_pickup
3
2
ready_for_pickup
4
2
ready_for_pickup
5
2
Here's the query I wrote to try to achieve what I'm looking for based on examples and research I did during the past couple of days. I find that sometimes there is sometimes a mismatch with the latest status that relates to the shipment
SELECT
statuses.slug AS slug,
MAX(shipments.id) AS shipment_id,
statuses.id AS status_id,
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
GROUP BY
`shipment_id`
Because we need to reference other fields from the same record that evaluates from the MAX aggregation, you need to do it in two steps, there are other ways, but I find this syntax simpler:
SELECT
shipments.id AS id,
statuses.slug AS slug,
statuses.id AS status_id,
shipment_status.shipment_id as shipment_id
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
WHERE
shipment_status.id = (
SELECT MAX(shipment_status.id)
FROM `shipment_status`
WHERE shipment_status.shipment_id = shipments.id
)
try it out!
This query makes the assumption that the id field is an identity column, so the MAX(shipment_status.id) represents only the most recent status for the given shipment_id
You can use window functions:
SELECT s.id, st.slug, st.id
FROM shipments s JOIN
(SELECT ss.*,
ROW_NUMBER() OVER (PARTITION BY shipment_id ORDER BY ss.id DESC) as seqnum
FROM shipment_status ss
) ss
ON ss.shipment_id = s.id JOIN
statuses st
ON ss.status_id` = st.id
WHERE ss.seqnum = 1;
Also note the use of table aliases so the query is easier to write and to read.

Select sum of zero if no records in second table?

I did some research and learned about the COALESCE(sum(num), 0) function. The issue is the example I found only related to using one table.
I am calculating a sum from a second table, and if there are no records for an item in the second table, I still want it to show up in my query and have a sum of zero.
SELECT note.user, note.product, note.noteID, note.note, COALESCE(sum(noteTable.Score), 0) as points
FROM note, noteTable
WHERE note.user <> 3 AND note.noteID = noteTable.noteID
I am only recieving results if there is an entry in the second table noteTable. If there are scores added for a note, I still want them to show up in the result with a points value of zero.
Table Examples:
Note
user | product | noteID |note
3 1 1 Great
3 2 2 Awesome
4 1 3 Sweet
NoteTable
noteID | score
1 5
The query should show me this:
user | noteID | sum(points)
3 1 5
3 2 0
4 3 0
But I am only getting this:
user | noteID | sum(points)
3 1 5
http://sqlfiddle.com/#!9/aae812/2
SELECT
note.user,
note.product,
note.noteID, note.note,
COALESCE(sum(noteTable.Score),0) as points
FROM note
LEFT JOIN noteTable
ON note.noteID = noteTable.noteID
WHERE note.user <> 3
and I guess you should add:
GROUP BY note.noteid
if you expect to get SUM for every user. So you want to get more then 1 record back.
First, learn to use proper JOIN syntax and table aliases. The answer to your question is SUM() along with COALESCE():
SELECT n.user, n.product, n.noteID, n.note,
COALESCE(sum(nt.Score), 0) as points
FROM note n LEFT JOIN
noteTable nt
ON n.noteID = nt.noteID
WHERE n.user <> 3
GROUP BY n.user, n.product, n.noteID, n.note;
You also need a GROUP BY.

showing one column several times with data from 3 different date intervals

I am trying to show three different figures of the same column In a mysql query and joining all three tables, I would like to keep one month static: April, so it would be a case like this I want to show The current month, the previous month and the static month of the year I'm working with, in this case let us stick with 2012
table: persons
ID name
1 Carl
table: vehicle
ID v_name person_veh
100 Dodge Viper 1
Table:payment
pay_id , pay_date, amount person_id
1 2012-02-12 1000 1
2 2012-03-11 780 1
3 2012-04-15 890 1
4 2012-05-12 1200 1
5 2012-06-12 1890 1
6 2012-07-12 1350 1
7 2012-08-12 1450 1
So what I want to do is show the column amount for the month of April as I said I want to keep that row static: 890, the current month lets say the current month is August:1450 and the previous month amount which would be July:1350: so the final result would be something like this:
name v_name april_amount current_month_amount previous_month_amount
Carl Dodge viper 890 1450 1350
You can use the following - which uses an aggregate function with a CASE statement:
select p.name,
v.v_name,
sum(case when Month(py.pay_date) = 4 then amount end) april_amount,
sum(case when Month(py.pay_date) = Month(curdate())
then amount end) current_month_amount,
sum(case when Month(py.pay_date) = Month(curdate())-1
then amount end) previous_month_amount
from persons p
left join vehicle v
on p.id = v.person_veh
left join payment py
on p.id = py.person_id
group by p.name,
v.v_name
see SQL Fiddle with Demo
Try
SELECT a.name, b.pay_date, b.amount, b.person_id, c.v_name
FROM persons a
LEFT JOIN payment b on a.id = b.person_id
LEFT JOIN vehicle c on a.id = c.person_veh
This query assumes id, person_veh and person_id are the like columns which links this user across these three tables

Complex query involving timestamps

I'm having some trouble with a complex query involving the following tables. Assume time is using the built-in sqlite timestamp datatype.
I am trying to return the customers whose 2nd purchase is within 4 hours of their first purchase AND if it's within 2 hours it must be from a different store.
I'm having trouble wrapping my head around how to refer to the specific rows to compare a first purchase with a second purchase.
purchases
purchase_id | customer_id | store_id | purchase_time
1 1 1 2009-01-27 10:00:00.0
2 1 2 2009-01-27 10:30:00.0
3 2 1 2009-01-27 10:00:00.0
4 2 1 2009-01-27 10:30:00.0
5 3 1 2009-01-27 10:00:00.0
6 3 2 2009-01-27 16:00:00.0
7 4 3 2009-01-27 10:00:00.0
8 4 3 2009-01-27 13:00:00.0
stores
store_id | misc columns...
1
2
3
customers
customer_id | f_name
1 name1
2 name2
3 name3
4 name4
The correct return would be name1, name4 in this case.
You're going to be joining the purchase table to itself, and then selecting on one of the two criteria.
The only real trick here is to formulate the different time criteria as:
Purchases that were made < 2 hours at different stores.
Purchases that were made between 2 and 4 hours, independent of store_id.
Both of which obviously apply for the same customer_id.
So, we've got:
select p1.purchase_id purchase_1,
p2.purchase_id purchase_2,
c.name,
p1.customer_id customer
from purchases p1
join purchases p2 on
p1.customer_id = p2.customer_id
join customer c on c.customer_id = p1.customer_id
where p1.purchase_time < p2.purchase_time
and (
(
addtime(p1.purchase_time,'2:00:00') >= p2.purchase_time
and p1.store_id <> p2.store_id
)
or
(
addtime(p1.purchase_time,'2:00:00') < p2.purchase_time
and addtime(p1.purchase_time,'4:00:00') >= p2.purchase_time
)
)
Which joins purchases to itself by customer_id, first checks that you're comparing earlier purchases to later purchases, and then applies the two different criteria in the criteria that are ORed.
I find the time comparisons easiest to do with the addtime() and then comparing the results. Others may prefer other ways.
SQL Fiddle here: http://sqlfiddle.com/#!2/14dda/2
Results:
PURCHASE_1 PURCHASE_2 NAME CUSTOMER
1 2 name1 1
7 8 name4 4
--
EDIT: Perhaps, you'd get some efficiency by moving the p1.purchase_time < p2.purchase_time up into the join clause. This might be faster with lots of data, though the execution plans for this little amount of data are identical. You'd like the optimizer to eliminate all those cases where p1.purchase_time > p2.purchase_time before doing the more expensive comparisons. But that's somewhat beyond the basic question of ways to write this query.