Most efficient way of getting first payment method used by all users - php

Lets say I have a table in a MSSQL database called "Customers". I also have a table called "Orders", each of which contains a "CustomerID". What I want to do, is to generate a summary of what payment method (let's call that "PaymentMethod") was used for the first "Order" of every "Customer".
The method I have been using for this is to conduct my customer selection query...
SELECT <Columns> FROM Customers <WHERE>
...and then for each result, conduct a separate query to obtain the customer's first order's payment method:
SELECT TOP 1 PaymentMethod FROM Orders ORDER BY Timestamp ASC
This process has the benefit of obtaining the data I want in a very simple way that's easy to modify, but the huge disadvantage of meaning a query is carried out for every customer, which could mean tens-of-thousands of extra queries every single time!
Is there a more efficient way of doing this that I'm not thinking of? I'm racking my brain to think of a way of selecting directly from the "Orders" table to begin with, but the requirement for the query to not only group by "CustomerID" but also fetch the MIN() of "Timestamp" and then return "PaymentMethod" of the MIN() record doesn't seem to work?

You can use ROW_NUMBER for this:
SELECT PaymentMethod
FROM (
SELECT PaymentMethod,
ROW_NUMBER() OVER (PARTITION BY CustomerID
ORDER BY Timestamp ASC) AS rn
FROM Orders ) AS t
WHERE t.rn = 1
The above query picks the earliest-per-group record.

I guess this helps you.
SELECT C.* , O.PAYMENTMETHOD FROM Customers C
INNER JOIN Orders O ON O.CustomerID = C.CustomerID
WHERE O.OrderTime =
(SELECT TOP 1 OrderTime FROM Customers WHERE CustomerID = C.CustomerID) -- selects customer first order based on min time

Related

Add Column to one Table that is COUNT of another

I have a list of subscribers in table Subscribers. Every time they receive a copy of their subscription, a new record is created in Subscriptions_Fulfilments for each Subscribers.ID.
I can create a table showing each Subscriber ID and the number of copies they received with the following query:
SELECT Sub_ID, COUNT(Sub_ID) fcount FROM `Subscriptions_Fulfilments`
GROUP BY Sub_ID
But I need to create a compound query that returns Subscribers along with a column showing the COUNT(Sub_ID) of Subscriptions_Fulfilments.
So I have two questions:
A) How would you make a query to create a table that shows each Subscriber and the number of times they've received their subscription, based on the COUNT of that Subscriber's ID in Subscriptions_Fulfilments?
B) I'm operating under the assumption that a single MySql query accomplishing this would be more efficient than, say, running two queries, the one above and a SELECT * FROM Subscriptions, and combining the resulting arrays in PHP. I have a feeling I know the answer but I'd like to positively learn something new today.
Unfortunately, after too many tries, I'm clearly not good enough at queries for this and I have very little past the above query to show for it. I apologize if this ends up being a dup, I searched long and hard before asking, but it's quite difficult to search precisely for Query help...
Here is a simple example showing the Subscribers ID and the no of subscription they have received. Hope it helps.
Step 1: select the ids from the Subscriber table
Step 2: select the no of counts of subscriptions received by each subscriber.
Step 3: Join both the table ON the basis of ID.
SELECT SubId, noSub FROM
Subscribers sb JOIN (SELECT SubId AS sid, COUNT(*)AS noSub FROM Subscriptions_Fulfilments GROUP BY SubId)AS ss ON sb.SubId = ss.sid
One of the big advantages of a relational database is the ability to do joins and combinations of the data in your tables in a way that allows for this functionality without having to actually store it in a separate table.
You can accomplish this with a subquery like this:
SELECT Subscribers.name, fulfilments.count FROM Subscribers
INNER JOIN (
SELECT id, count(*) as count FROM Subscriptions_Fulfilments
GROUP BY Sub_Id
)fulfilments ON subscribers.id = fulfilments.id
This might not be 100% what you're looking for and I might have messed up your names, but hopefully this will start to get you in the neighborhood of being correct?
Simply try execute this query:
Select distinct Sub_ID, count from (SELECT Sub_ID, COUNT(Sub_ID) fcount FROM Subscriptions_Fulfilments
GROUP BY Sub_ID);

Using mysql statement to join tables and return maximum values instead of PHP maths

In PHP with MySQL I am trying to write queries to pull certain required reports with Joined Tables.I have read though so many SQL scripting manuals, and would normally do this type of thing by pulling filtered records from db tables into PHP, and doing the math gymnastics there. Unfortunately this is inefficient so I am looking for the right SQL query to point me in the right direction. This is my situation.
I have mysql tables: events and payments, they are linked by a single ID column (events.event_id = payments.payment_event_id)
I need to get a recordset containing only the 5 events with the most associated payments. Then I would like a result array containing this original event detail arrays along with an integer which is their ticket payment count.
Ideally the result array structure would look like this:
$result_events--
|0||0|payment count(53)
|0||1|$full event array
|1||0|Payment Count (34)
|1||1|$full event array
EDIT:
After talking to people I thought I would spell out what I am after. I need a 2 column resultset, left column is event_id (from events table) and right hand column is total amount of payments for that event (ie the number of 'payment' rows which match this event id. So out of huge random tables of events and payments, I would like to be left with one like this:
| Event_id | total Payments |
but ordered by the payment column. I know I'm asking the earth :)
If you want to get the 5 most common payments for an event and get just those 5 details, you can do a subquery to get the event_ids where you group the payment_event_ids, then order the count descending top to bottom and take the top 5 results.
Then you take those ID's and get the event details that match those ids
SELECT
*
FROM
events
WHERE
event_id IN (
SELECT
payment_event_id
FROM
payments
GROUP BY payment_event_id
ORDER BY count(*) DESC
LIMIT 5
);
Optionally you can also get all the payment details with a join, but you'll get more than 5 rows returned this way
SELECT
*
FROM
events
JOIN
payments
ON
events.event_id = payments.payment_event_id
WHERE
events.event_id IN (
SELECT
payment_event_id
FROM
payments
GROUP BY payment_event_id
ORDER BY count(*) DESC
LIMIT 5
);
Simple query, only thing left for you to do is select the columnnames you need from both tables:
$q = "SELECT events.columname, payments.columnname FROM events
INNER JOIN payments ON events.event_id = payments.payment_event_id
ORDER BY payments.payment_count DESC
LIMIT 5";

Updating table A with information from table B (which contains duplicate information)

This is a bit tricky. I've got someone with a very specific request.
This database has two tables: Table A (customer table) and table B (order table).
Table A has a unique customer ID (customerid) for each row. It also has a Date of Service (DATEOFS) column, which is primarily populated with old information from the previous database.
Table B has a unique Order number (ordernum) for each order. Each order is also linked to the customer (customerid) and has the date of the order (dofserv).
To show the Date of Service in the actual PHP script, I use the following query:
SELECT dofserv FROM orders WHERE customerid=" . $result['customerid'] . " ORDER BY dofserv DESC LIMIT 1
This returns me the most recent date of service for a single customer.
What I'm attempting to do is update the DATEOFS in Table A with the -most recent- DOFSERV from Table B.
As I understand it, the following query would work if there was only one matching row:
UPDATE clients a, orders b SET a.DATEOFS = b.DOFSERV WHERE a.customerid = b.customerid
However, since the orders table has multiple rows which match the customerid, I'm not sure how to update the clients.DATEOFS with only the most recent orders.DOFSERV.
Any suggestions? Would like to do this in pure MySQL to run from the command line; I could do it in PHP, but I'm not sure how long it would take to run.
One way to do it is like this:
UPDATE clients a
SET
a.DATEOFS=(SELECT MAX(b.DATEOFS) FROM orders b WHERE a.customerid=b.customerid);
But this query will insert NULL in a.DATEOFS if there is no matching order. If that's not what you want, you can modify it slightly to:
UPDATE clients a
JOIN orders o ON a.customer_id=o.customer_id
SET
a.DATEOFS=(SELECT MAX(b.DATEOFS) FROM orders b WHERE a.customerid=b.customerid);
This query will restrict the updates to only those rows in clients where there is at least one matching row in orders.
You could join the clients table on an aggregate query:
UPDATE clients a
JOIN (SELECT customer_id, MAX(dofserv) AS md
FROM orders
GROUP BY customer_id) b ON a.customerid = b.customerid
SET a.dateofs = b.md
This is one way that will only update a minimal number of rows:
update clients a
set dofserv = ( select max(dofserv)
from orders b
where b.customerid = a.customerid
)
where exists ( select *
from orders b
where b.customerid = a.customerid
and b.dofserv > a.dofserve
)

Get and order based on most recent entry while in multiple mysql joins

I have the following statement that finds all the players of a team in the current season. The players are ordered by their handicap. If their handicaps are the same they are ordered by the oldest added_date, meaning newer members are lower down the list.
SELECT players.playerid_p,
players.fname,
players.sname,
players.tel,
players.mob,
players.email,
season_players.captain
FROM season_players
LEFT JOIN players ON (season_players.playerid_f = players.playerid_p)
LEFT JOIN handicaps ON (handicaps.playerid_f = players.playerid_p)
WHERE season_players.seasonid_f = '$currentSeason'
AND season_players.teamid_f = '".$row["teamid_p"]."'
GROUP BY players.playerid_p
ORDER BY handicaps.handicap ASC, handicaps.added_date ASC
The handicaps table can have multiple entries per player for any reviews they have had.
I can't figure out how to make the latest handicap to be used for the ordering (something maybe to do with MAX on added_date?) yet at the same time if two or more handicaps are the same it order them by oldest registered first based on added_date.
In stead of trying to figure out how to satisfy your needs in current query, wouldn't it be an option to create a separate table, let's say "Handicaps_latest", which only stores player_id and required info of latest review. The reason for doing this is because you are only trying to get(columns in your select clause) the information about players and nothing really needed from handicaps table. In this case, a handicaps table with multiple entries per player might not be a good table to join. But considering that those data might be required in other logic, so leave them there and create a branch new table only storing latest review data could be an option for your case. But it requires some extra work apparently, that is, whenever you insert a new entry into your original handicpas table, a particular entry in handicaps_latest needs to be updated.
SELECT x.*
FROM my_table x
JOIN
( SELECT id,MAX(other_column) max_other_column FROM my_table GROUP BY id) y
ON y.id = x.id
AND y.max_other_column = x.other_column;

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