I need help creating a SQL query - php

I need to come up with a report that looks something like this:
There are 3 tables Agents, Sales and Payments. Sales and Payments are linked via the Agents table. The report is to show the total daily sales per Agent per day. It also needs to show for each day any payments the agent has made and this is to be deducted from the total sales balance such that any any point in time we can tell if the Agent owes the business or vice versa.
The way I plan to tackle this is:
Get all sales per agent per day (one query)
Get all payments per agent per day (one query)
For each Sales, agent and each day check (agents payments per agent per day) (loop)
If match is found get value and use for calculation with cumulative total
Otherwise just display appropriate sale data only with cumulative total.
I'm not sure if there is query that can make my life a bit easier. Thanks.
[Edit]
Agents Table:
id,
name,
opening_balance
Sales Table:
id,
agent_id,
amount,
customer_id,
sale_date
Payments Table:
id,
agent_id,
amount,
bank_id,
payment_date,
status
[2nd Edit]
Thanks Olaf Dietsche. The second query without the sub-select took a long time to run. About 88 secs. I also tried the first on with the sub-select and it completed in a flash however the sum(s.amount) and sum(p.amount) hold values twice what they should be.
[3rd Edit]
An agent can make multiple sales and
An agent can make multiple payments
There is no direct connection between sales and payments

Here's a first cut
select a.name, s.sale_date, sum(s.amount), sum(p.amount),
(select sum(amount)
from sales
where agent_id = a.id and sale_date <= s.sale_date) as cum_sales,
(select sum(amount)
from payments
where agent_id = a.id and payment_date <= s.sale_date) as cum_payments
from agents a
join sales s on s.agent_id = a.id
join payments p on p.agent_id = a.id
group by s.agent_id, s.sale_date
order by s.agent_id, s.sale_date
The cumulative balance would be opening_balance - cum_sales + cum_payments.
SQL Fiddle
Here is a select without subselects. This might be faster
select a.name, s.sale_date, sum(s.amount), sum(p.amount),
sum(cs.amount) as cum_sales,
sum(cp.amount) as cum_payments
from agents a
join sales s on s.agent_id = a.id
join payments p on p.agent_id = a.id
join sales cs on cs.agent_id = a.id and cs.sale_date <= s.sale_date
join payments cp on cp.agent_id = a.id and cp.payment_date <= s.sale_date
group by s.agent_id, s.sale_date
order by s.agent_id, s.sale_date
SQL Fiddle
Both are untested, since I don't have data to do so.

Related

Mysql Minus Sum Of Two Different Columns With Join - Using Symfony Query Builder

I've got two tables:
Account: id, name
Transaction: account_id,payment,charge
The transactions links to the account, and the transaction can either be a payment or a charge. What I'm looking to do is in a single query, sum all the payments and minus the sum of all the charges so give the overall balance.
I'm not great with mysql queries when they get this complex, but I can get the sum of the payments easily enough:
SELECT SUM(I.payment), A.* FROM propertyLettingAccountNew A INNER JOIN lettings_account_statement_item I ON I.letting_account_id = A.id GROUP BY A.id
So this is giving me the over all payment amount but I'm not sure how to sum the charge column and subtract it from the payment sum. I'm going to be using the symfony query builder for running this query once it's figured out if that makes a difference.
You can subtract charge from payment for each transaction and sum the results:
SELECT a.id, a.name,
SUM(COALESCE(t.payment, 0) - COALESCE(t.charge, 0)) balance
FROM Account a INNER JOIN Transaction t
ON t.account_id = a.id
GROUP BY a.id, a.name
I use COALESCE() just in case there are nulls in the columns.
If there aren't any nulls:
SELECT a.id, a.name,
SUM(t.payment - t.charge) balance
FROM Account a INNER JOIN Transaction t
ON t.account_id = a.id
GROUP BY a.id, a.name

Crossreferencing two SQL results, finding overlap?

I have a table of customer orders: inside are zero cost orders (things I have given away to customers) as well as paid orders. I want to find out how many customers who have received a free order have also purchased an order.
SELECT customer_id
FROM orders
WHERE total = 0
GROUP BY customer_id
SELECT customer_id
FROM orders
WHERE total != 0
GROUP BY customer_id
These two queries give me a list of customers who received a free order and a list of customers who paid for an order. I want to count the overlapping instances, for example:
//free orders
1,5,2,9,3,11,7
//paid orders
1,5,4,8,7,9,3,12,10,13
The intersect between these two sets is {1,5,9,3,7}, which is five element. I need a way to do this strictly using SQL preferred.
Just use a having clause. This is probably the simplest way, assuming total has no negative values:
SELECT customer_id
FROM orders
GROUP BY customer_id
HAVING MIN(total) = 0 AND MAX(total) > 0;
Try this (not tested, sorry)
SELECT count(distinct customer_id)
FROM orders a JOIN orders b
ON a.customer_id = b.customer_id
WHERE a.total = 0 and b.total <>0

fast and slow moving products in mysql

Can anyone help me out about fast and slow moving products on MySQL?
I want to know how to SELECT CLAUSE all the fast moving products and the slow moving products seperately. Here are my tables.
**product**
productID
productname
price
**sales**
transactionID
productID
quantity
subtotal
**transaction**
transactionID
datetransact
I cut some of the columns to make it look simple.
FAST MOVING PRODUCTS is a product that have been sold often in a specific period of time.
SLOW MOVING PRODUCTS is a product that sell not so often and sit on the shelves on a long period of time.
You will want to group by product and select the min(datetransact) and max(datetransact). The difference of these two will give you the number of products sold and the timespan between the first and last sale date. Then you can divide these to get an average.
Updated to calculate on quantity sold.
select sum(sales.quantity) as productssold,
min(transaction.datetransact) as firstsale,
max(transaction.datetransact) as lastsale,
max(transaction.datetransact) - min(transaction.datetransact) as timespan,
sum(sales.quantity) / max(transaction.datetransact) - min(transaction.datetransact) as averagesold
from product
join sales on product.productid = sales.productid
join transaction on sales.transactionid = transaction.transactionid
group by product.productid
having averagesold >= 'desired value'
Scott's answer is good as far as it goes. First, you seem to be concerned about the quantity of the products sold not just the number of transactions containing the product. And, the question (which has perhaps been revised) is about a particular date range.
To get the answer for a particular range of dates, simply use a where clause or conditional aggregation. The following uses filtering and includes products with no sales:
select p.*, sum(s.quantity) as productssold,
sum(s.quantity) / datediff(#datelast, #datefirst)) as AvgPerDay
from product p left join
sales s
on p.productid = s.productid left join
transaction t
on s.transactionid = t.transactionid
where t.datetransact between #datefirst and #datelast
group by p.productid
order by AvgPerDay;
If you don't want products that never sold, simple change the left join back to inner joins.
The problem with the filtering approach is that some products may have had their first sale after beginning of your period. To handle this, you want to measure the average since the first sales date (or perhaps since some release date in the product table). This basically moves the date condition from the where clause to the having clause:
select p.*, sum(case when t.datetransact between #datefirst and #datelast then s.quantity else 0 end
) as productssold,
(sum(case when t.datetransact between #datefirst and #datelast then s.quantity else 0 end) /
datediff(#datelast, least(#datefirst, max(t.datetransact)))
) as AvgPerDay
from product p left join
sales s
on p.productid = s.productid left join
transaction t
on s.transactionid = t.transactionid
group by p.productid
order by AvgPerDay;

MySQL query exclude entry depending on date

I need help with a MySQL query. I have 2 tables, clients and payments. The table clients has 2 fields id and name. The table payments has id, id_clients, pay_month. The clients need to pay every month a certain amount.
I want to search only the name of clients which has not paid the current month.
my query
SELECT name FROM clients WHERE id NOT IN (SELECT DISTINCT id_client FROM payments WHERE pay_month > '2014-03-15' ORDER BY pay_month DESC)
I managed to resolve my query.
I used php to store the date $today = date("Y-m-d");
SELECT DISTINCT clients.id, clients.name FROM clients WHERE clients.id NOT IN (SELECT DISTINCT id_clients FROM payments WHERE pay_month > '".$today."' ORDER BY pay_month DESC) ORDER BY clients.name ASC
Thank you everyone for your help.
Maybe something like this.
SELECT
c.name as client_name,
p.pay_month as payment_month
FROM clients c
LEFT JOIN payments p on p.id_clients = c.id
WHERE p.id IS NULL AND YEAR(p.pay_month) >= YEAR(NOW()) AND MONTH(p.pay_month) > MONTH(NOW());
if you could give us some sample data i can build a sql fiddle and do it much easier.
just edited to fix the date format/check

Find New Buyers, and Returning Buyers from the Orders tables

I have an existing E-commerce database with the rather standard sales tables. The relevant tables are:
Orders table.
The fields are like:
OrderID, CustomerID, OrderDate, ...
Customers table.
CustoerID, CustomerFirstName, CustomerLastName, ...
I need to find two values, namely:
Total new buyers (within a certain time period *)
Basically, those are fist time buyers within a certain time period
Total returning buyers (within a certain time period *)
Basically, these are buyers who have bought before, prior to the time period
time period, we will provide as the inputs, such as within 1 week
My database is in MySQL.
Question:
What is the easiest and most efficient way to get the two totals?
1. Total new buyers
2. Total returning buyers
Do I need to write a program in PHP? Or I can simply use SQL statements to achieve this?
Thanks for any help.
This can be done purely in SQL:
Number of First time buyers:
SELECT
COUNT(DISTINCT CustomerID)
FROM Orders
WHERE OrderDate BETWEEN <startdate> AND <enddate>
/* Buyers with only one order record */
AND CustomerID IN (SELECT CustomerID FROM Orders GROUP BY CustomerID HAVING COUNT(*)=1)
Number of repeat buyers:
SELECT
COUNT(DISTINCT CustomerID)
FROM Orders
WHERE OrderDate BETWEEN <startdate> AND <enddate>
/* Buyers with more than one order record */
AND CustomerID IN (SELECT CustomerID FROM Orders GROUP BY CustomerID HAVING COUNT(*)>1)
Both! You have to write SQL statement that return data from the Database and call the statement from inside a PHP script to deal with it. The SQL statement make you able to retrive data and the PHP code make you able to menage and eventually presetn data on your web page (cenverting it into HTML language).
This is the common scenario, but if you need a more detailed procedure with code, use google and have a nice coding!
Total new buyers
SELECT c.*
FROM Customers c, Orders o
WHERE c.CustomerID = o.CustomerID
AND c.CustomerID not in(SELECT o1.CustomerID from Orders o1)
Total returning buyers
Total new buyers
SELECT c.*
FROM Customers c, Orders o
WHERE c.CustomerID = o.CustomerID
AND c.CustomerID in(SELECT o1.CustomerID from Orders o1)
you can add time frame to both queries

Categories