Identify customers based on their # of transactions - ms-access

I have an MS access table which has the following structure
Trans ID*|Cust ID|Qty|Value
----------------------------
001|A201|5|100
----------------------------
002|B501|2|20
----------------------------
003|A201|2|30
----------------------------
004|A201|-1|-10
----------------------------
005|A201|5|500
----------------------------
I need to find out the list of customers who have got more than 3 transactions (excluding transactions which have negative quantity). Thanks in advance.

Did take some in-house help, and I found the answer. Thanks for the hints every one.
select COUNT(CUST) from (select cust_id as CUST, count(ID)
as NUMTRANS from Transactions where Qty>0 group by cust_id) where NUMTRANS>=3

Related

Histogram using to data tables (SQL query)

I want to make a histogram of the number of comments per user in January 2019 (including the once that haven't commented)
The tables I'm working with look like this:
id
Name
1
Jose
2
Pedro
3
Juan
4
Sofia
user_id
Comment
Date
1
Hello
2018-10-02 11:00:03
3
Didn't Like it
2018-06-02 11:00:03
1
Not so bad
2018-10-22 11:00:03
2
Trash
2018-7-21 11:00:03
I think I'm overcomplicating it. But here is my try:
#Here I'm counting how much comments are per person that have commented.
CREATE TABLE aux AS
SELECT user_id, COUNT(user_id)
FROM Undostres
GROUP BY user_id;
#With the following code, I end up with a table with the missing values (ids that haven't commented)
CREATE TABLE Test AS
SELECT DISTINCT user_id +1
FROM aux
WHERE user_id + 1 NOT IN (SELECT DISTINCT user_id FROM aux);
ALTER TABLE Test RENAME COLUMN user_id +1 TO ;
INSERT INTO Undostres (user_id)
SELECT user_id FROM Test;
It returns an error when I try to rename user_id+1 with other name. So I can't keep going.
Any suggestions would be great!
I would do it this way:
CREATE TABLE aux AS
SELECT Users.user_id, COUNT(Undostres.user_id) AS count
FROM Users
LEFT OUTER JOIN Undostres USING (user_id)
GROUP BY Users.user_id;
I am assuming you have a table Users that enumerates all your users, whether they have made any comments or not. The LEFT OUTER JOIN helps in this case, because if there are no comments for a given user, the user is still part of the result, and the COUNT is 0.

How to query scalable prices in MySQL

Dear stack overflow community,
This is my first post so please bear with me :)
I need to solve a SQL problem for a friend of mine.
He is running a web shop and wants to create a finance report.
The application he is using provides such functionality using an interface were MySQL queries can be executed.
I already created most of the report with my (limited) SQL knowledge, however I am struggeling to solve the last problem.
The goal of the report is to UNION and JOIN several tables to get an overview of all commissions, invoices and proposals with their respective articles and prices.
So what I did so far:
I did a union of commissions, invoices, proposals (lets call them receipt) and joined them with articles and prices.
That worked very well.
However, here is my problem:
An article could have multiple prices depending on the date of the respective receipt.
So I end up with more rows in my table as there should be.
There is a "valid_until" field within the prices table, which I have to use for the filter ... but how?
Example:
receipt_id
receipt_date
article_id
article_price
valid_until
price_id
209986-1
2020-09-10
2925
13
2020-12-06
1
209986-1
2020-09-10
2931
13
2020-09-09
2
209986-1
2020-09-10
2937
12,6
2020-09-12
3
209986-1
2020-09-10
2980
12,32
0000-00-00
4
In this case, only price_id 3 is valid as the receipt_date is "2020-09-10".
My Query (with limited SQL knowledge):
SELECT *
FROM (SELECT * FROM commissions UNION ALL SELECT * FROM invoices UNION ALL SELECT * FROM proposals) AS receipt
LEFT JOIN article ON receipt.article = article.id
LEFT JOIN prices ON article.id = prices.artikel
WHERE receipt.date <= IF(prices.valid_until = '0000-00-00', Date('3000-01-01'), prices.valid_until)
With that query I still get 3 results (price_id 4, 3 and 1).
I managed to identify the valid price using DATEDIFF(), ORDER BY and LIMIT, however MySQL does not allow to use LIMIT in sub-queries :(
Any help would be much appreciated.
KR,
Wlad

How can I repeat this query over several months in MySQL?

I have a membership list with over 1.2M members. People commonly subscribe, unsubscribe, and re-subscribe to the list. Often, I find myself needing to know which users were subscribed at a particular moment in time. I have a table called subscription_history, with this structure:
-----------------------------------------------------------------------
| id | native key |
-----------------------------------------------------------------------
| user_id | foreign key that joins the user table |
-----------------------------------------------------------------------
| change_code | 1 or 2 for subscriptions, 4-7 for unsubscriptions |
-----------------------------------------------------------------------
| created_at | date-time stamp when the change was made |
-----------------------------------------------------------------------
Right now, if I want to know who was subscribed at a particular date in the past (March 31, 2012 in this example), I run this query:
SELECT user_id
FROM
(SELECT
user_id
, MAX(created_at) AS last_change_date
, change_code
FROM subscriptionhistory
WHERE DATE(created_at) <= '2012-03-31'
GROUP BY user_id
) AS last
WHERE change_code IN (1,2)
This finds each user's last subscription action before or on the target date, then returns the user if that action was a subscription. We then use that list of users to run various other queries, such as the average lifetime sales. This system works well, but only for one date at a time. If I wanted to know the average subscriber's lifetime sales for every month of the year, I would have to run this query 12 times, manually incrementing the date in the WHERE statement each time.
Now I want to create a version of this that can I can use for more than a single date... so that it could give me all users subscribed in January, then February, etc., and I could run average lifetime sales for subscribers in each month. I can't just do a GROUP BY for this, since someone who was a subscriber in March might have unsubscribed in April and re-subscribed in June. I suppose I could 12 UNION queries ... but was hoping for something a little more elegant!
A few limiting parameters: I only have read-only access to the database; I cannot change anything about the table structure or make temporary tables. I have to do this only in MySQL - because of the way our CRM works, I can't use Python or PHP to manipulate results. Any help would be greatly appreciated! Please let me know if I am not explaining this well. Thanks!
SELECT user_id, group_concat(date_format(created_at, '%Y-%m')) as ActiveMonth from
(SELECT user_id, created_at, change_code from Subscriptions WHERE
change_code in (1,2) order by 1,2,3) b
group by user_id
order by user_id, ActiveMonth desc
You can take the group_concat out and the group by and it should give you a row and active month for every user_id.
I created a SQLFiddle and changed the table name to subscriptions for ease of use.
http://sqlfiddle.com/#!2/6b2f2/14

Select table entries belonging to combinations of entries in another table (mysql)

Regarding MySQL queries, how can I get all table entries that belong to combinations of entries in another table?
Background: I would like to count orders which consist of order items. Each order item has a state like 'canceled' or 'delivered'. There are partial deliveries, so that there can be both delivered and canceled orders in one order. I would like to count the net orders and I would like to know how many orders have items with more than one item status.
Order Number | Order Item | Status
X0001 | Item | delivered
X0001 | Item | delivered
X0002 | Item | delivered
X0002 | Item | canceled
X0002 | Item | delivered
X0003 | Item | delivered
I have 3 valid orders here and one order with delivered/canceled. How can I ask for all orders which have at least one delivered and one canceled item?
As I am very new to MySQL I am basically looking for the right approach. Do I need subqueries or joints for this?
Edit: First of all, sorry for the late reply. And sorry again because my question was obviously misleading.
There are three tables involved: 'order', 'order_item' and 'order_item_status'.
'order' and 'order_item' are linked through 'order_item.fk_order' and 'order.id_order'.
'order_item' and 'order_item_status' are linked through 'order_item.fk_order_item_status' and 'order_item_status.id_order_item_status'.
You have been very helpful so far but I am still a bit stuck as I do not know how to finally count by combination.
The perfect result would be something like that:
{shipped} | 34
{canceled} | 12
{shipped, canceled} | 8
{closed, canceled} | 4
{closed} | 27
... | ...
But I don't know how to deal with combinations in a query. Maybe you have some more helpful hints for me ...
Thank you very much.
I assume you have two table:
orders (list of your orders)
order_items (list of itemd of order)
two table are relationed by field fk_order.
try this:
SELECT o.id, (select count(*) from order_items i where i.status = 'delivered' and i.fk_order = o.id) as item_delivered, (select count(distinct i.status) from order_items i where i.fk_order = o.id) as status_no
FROM orders o
I assume your data are all in one table like you described. Then you want to group your data by order number and then only get the orders with both canceled and delivered ?
The request should also look like :
select * from table \
group by Order Number \
Having State="canceled" and State="delivered"
By join on the table with the same table you can select rows with different values. Then count the distinct order numbers.
SELECT COUNT(DISTINCT orig.Number)
FROM order_items AS orig
INNER JOIN order_items AS diff ON diff.Number = orig.Number AND diff.Status != orig.Status
This is quicker than using sub-queries.
Edit: Forgot to mention it's a more scalable implementation as well as you can add more order statuses without this failing.
I don't understand why you are talking about "combinations of entries in another table" ?
According to the following of your question you have one single table with - at least - 3 attributes: Order number, Order item, Status. Is that correct or are some of those attributes in another table and in that case can you elaborate ?
Otherwise I would try to use sub-requests and do something like:
SELECT T1.* FROM table T1 WHERE T1.status=canceled AND T1.order_number IN (SELECT T2.order_number FROM table T2 where T2.status=delivered)

Summary of a day or run a query and sum the results?

Since I don't know to calculate efficiency I'll ask here and I hope someone could tell me what is better and explain it a bit.
The scenario:
Currently I have a table that insert rows of production of each worker.
Something like: (Worker1) produced (product10) with (some amount) for a Date.
And that goes for each station he worked in though the day.
The Question:
I need to generate a report of the sum of amounts that worker produced for each date. I know how to generate the report either way but the question is how is it more efficient?
Having to run a query for each person that sums up the production for each date? or having a table that I'll insert the total amount, workerID and date?
Again if you could explain it a bit further it would be nice, if not than at least an educated answer would help me a lot with this problem.
Example:
This is what I have right now in my production table:
ID EmpID ProductID Amount Dateofproduction
----------------------------------------------------------
1 1 1 100 14/01/2013
2 1 2 20 14/01/2012
This is what I want in the end:
EmpID Amount DateofProduction
-----------------------------------
1 120 14/01/2013
Should I start another table for this? or should I just sum what I have in the production table and take what I need?
Bear in mind that the production table will get larger and larger each day (of course).
i) Direct :
select EmpId, sum(Amount) as Amount, DateOfProduction
from ProductionTable
group by EmpId, DateOfProduction.
ii) Now, the size of the table will keep growing. And you need only day-wise reports.
Is this table being used by anyone else? Can some of the data be archived? If some of the data can be archived, I would suggest, after each day and reporting, backup all the data from this table to a secondary archive table. So, every day you will have to query only today's worth of records.
Secondly, you can consider adding an index to DateOfProduction. You will then be able to restrict your queries in date range. For example, select EmpId, sum(Amount) as Amount, DateOfProduction from ProductionTable group by EmpId, DateOfProduction where DateOfProduction = Date(now()). (or something similar)
Because it is just a single table and no complicated queries, MySql will be easily able to take care of millions of records. Try EXPLAIN on the queries to check the number of records being touched and indexes being used.
Unless I am missing something, it sounds like you just want this:
select empid,
sum(amount) TotalAmount,
Dateofproduction
from yourtable
group by empid, Dateofproduction
See SQL Fiddle with Demo
Result:
| EMPID | TOTALAMOUNT | DATEOFPRODUCTION |
------------------------------------------
| 1 | 120 | 2013-01-14 |
Note: I am guessing that the second row of data you provided is supposed to be 2013 not 2012.