SQL count based on timediff and column containing a string - mysql

I'm using SQL Workbench.
cust_num date notes
1234 2016-02-01 advice
1234 2016-02-01 something else
1234 2016-02-02 order
1234 2016-02-03 order
4421 2016-02-15 advice
4421 2016-02-17 order
4421 2016-02-18 something else
4421 2016-02-24 order
I know the above is a bit unclear, but basically, there's 3 columns in the above table. One showing customer_num (customer number), one showing date and one showing a notes field.
From the above, I want to perform two queries. I am newish to this so, I hope this is clear. I'm using SQL workbench.
i) I want to count the number of DISTINCT 'customer_num's that placed an order within 4 days of receiving advice.
So the answer based on the table above would be 3. This is because cust_num '1234' made two orders within 4 days and cust_num '4421' made 1 order. So that totals 3
ii)I want to count the number of DISTINCT customer_num's that placed an order within 15 days of receiving advice. Only stipulation is that I don't want to re-count those from (i) that placed an order within 4 days. I want to exclude them.
So the answer to this would be 1. Customer_num '4421' placed 1 order that was bigger than 4 days but smaller than or including 15 days.
Any help really appreciated. Thank you.

One method is to use exists:
select count(distinct cust_num)
from customers t
where exists (select 1
from customers t2
where t2.cust_num = t.cust_num and
t2.date between t.date and date_add(t.date, interval 3 day)
);
The two queries have the same structure. You just need to change the condition in the where clause in the subquery.

Related

MYSQL Group in one table and multiply with numbers from another table

I have two tables as below:
goods_in:
in_id|pid|in_num|in_date
1 1001 10 2020-06-28
2 1002 20 2020-06-28
3 1001 20 2020-05-25
......
stock:
stock_id|pid|num|price
1 1001 10 5
2 1002 15 6
3 1003 20 7
...
The "goods_in" table stores the records that all goods come into warehouse, the pid in this table is the same as in table "stock" which is the product ref code. There will be multiple records for each product in "goods_in" table.
The "stock" table stores all the SKU we are holding and the current stock level as well as the product cost.
What I'm trying to do is:
Group by pid AND date (Year+Month) from "goods_in" so I get sub-total number of goods booked-in in each month.
Multiple sub-total with stock.price.
Get total amount of above, something like SUMPRODUCT in excel.
Exports to html table or excel.
I've tried several answers from SO with GROUP BY/ROLLUP/JOIN, and apparently I haven't made it right as expected. I can make this simple if I just add a cost column to the "goods_in" table but that would make it untidy.
I'm still on my learning curves with MYSQL, forgive me if this looks simple to you guys.
Thanks.
Ken
Hard to know for sure what you want, but it sounds something like this:
select
pid,
year(in_date) as year,
month(in_date) as month,
sum(goods_in.in_num * stock.price)
from goods_in
join stock using (pid)
group by pid, year, month
For exporting to html or excel, you are best off asking a separate question.

How to Query the Same Data within a Table but the Output Row Positions are Different

I have a table inside my database just like the sample below and i would like to query the same data but in the Column 2 the positions of the data would be 1 row greater than the previous data.
P.S. Im actually making a system for a Electric Meter Reading and I need the Current(Column 1) and the Previous(Column 2) Data Reading, so that I could compute the total consumption of the Electric Meter. But I am having a hard time doing it. Any suggestions would be deeply appreciated. Thank You. :)
Example data:
Desired Query Output:
Keep in mind that SQL table rows have no inherent order. They're just bags of records.
You must order them based on some column value or other criterion. In your case I guess you want the most recent and the second most recent meter reading for each account. Presumably your reading table has columns something like this:
reading_id customer_id datestamp value
1 1122 2009-02-11 112
2 1234 2009-02-13 18
3 1122 2009-03-08 125
4 1234 2009-03-10 40
5 1122 2009-04-12 160
6 1234 2009-04-11 62
I guess you need this sort of result set
customer_id datestamp value previous
1122 2009-03-08 125 112
1122 2009-04-12 160 125
1234 ...etcetera.
How can you get this? For each row in the table, you need a way to find the previous reading for the same customer: that is, the row with
the same customer id
the latest datestamp that occurs before the current datestamp.
This is a job for a so-called correlated subquery. Here's the query, with its subquery. (https://www.db-fiddle.com/f/hWGAbq4uAbA5f15j7oZY9o/0)
SELECT aft.customer_id,
aft.datestamp,
( SELECT bef.value
FROM r bef /* row from table.... */
WHERE bef.datestamp < aft.datestamp /* with datestamp < present datestamp */
AND bef.customer_id = aft.customer_id /* and same customer id */
ORDER BY bef.datestamp DESC /* most recent first */
LIMIT 1 /* only most recent */
) prev,
aft.value
FROM r aft
ORDER BY aft.customer_id, aft.datestamp
Notice that dealing with the first reading for each customer takes some thought in your business process.

How to sum specific rows and columns in SQL?

pnr mnd pris
1 1 600
1 7 900
2 1 600
2 7 600
3 1 40
3 7 40
I have trouble how to sum specific rows on the columns. Looking at the above, the table is called travel and it has 3 columns:
pnr - Personal Number
mnd - Month
Pris - Price
So what I want is to sum total of the price for the a specific month, so in this case, it should be 1240 USD and month 1. For the month 7, it should be 1540 USD.
I have trouble to do the query correct. So far from I have tried is this:
SELECT t.rnr, t.mnd, SUM(t.pris)
FROM travel AS t
WHERE t.mnd = 1
The result I get is 3720 USD which I have no idea how the SQL managed to calculate this for me.
Appreciate if someone could please help me out!
For this you need to drop the pnr column from the output (it is not relevant and will cause your data to split) and add a GROUP BY:
SELECT t.mnd, SUM(t.pris)
FROM travel AS t
WHERE t.mnd = 1
GROUP BY t.mnd
Live demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b34ec2bb9c077c2d74ffc66748c5c142
(The use of an aggregate function without grouping, as you've got now, is not a standard SQL feature and can often be turned off in MySQL. If turned on, you might not always get the result you expected/intended.)
just group your result with mnd column
SELECT t.mnd, SUM(t.pris)
FROM travel AS t
group by t.mnd

Average date of visiting website

I'd like to know the average dates per week that users have been visited the website 'A'. If the user hasn't visited the website 'A', I exclude the data (e.g., id = 2). And I also need to consider the date range (limit it to a week range, e.g., 01-JAN-2018 to 07-JAN-2018)
Sample input (Table:User)
id date website
1 01-JAN-2018 A
1 03-JAN-2018 B
1 04-JAN-2018 C
1 04-JAN-2018 C
2 03-JAN-2018 C
3 03-JAN-2018 A
3 05-JAN-2018 B
4 05-JAN-2018 A
The first step will like this:
id date website
1 01-JAN-2018 A
1 03-JAN-2018 B
1 04-JAN-2018 C
1 04-JAN-2018 C
3 03-JAN-2018 A
3 05-JAN-2018 B
4 05-JAN-2018 A
The output will only return the average dates that users visiting websites (including ABC). In this case, user 1 visited three days a week (ignore duplicates) and user 3 visited two days a week. The average dates of hits will be sum(days)/number of users.
My first thought:
SELECT COUNT(Date), Date
FROM user
WHERE id IN (
SELECT id FROM user
WHERE web = 'A'
);
Assume that I only want to consider this week range (01-JAN-2018 to 07-JAN-2018). I want to figure out the average of dates of visiting in one week. Any thoughts for this? Thanks!
Link for Demo
If you want to group by hits in a week, you might try something more like this:
select year(STR_TO_DATE(date,'%d-%b-%y')) year,
weekofyear(STR_TO_DATE(date,'%d-%b-%y')) week,
count(*) hits
from user
group by year(STR_TO_DATE(date,'%d-%b-%y')), weekofyear(STR_TO_DATE(date,'%d-%b-%y'))
The group by is the key: this will group all the hits for a particular week together, whereas group by date will keep each day separate.
If you want an average for multiple weeks, you would need to use this query as a subquery, and do an average on the count column.
And as was stated in comments, this would be a LOT easier (not to mention more efficient) if the date was stored as a date and not as a varchar

SQL: how select all rows where a "count > 1" for certain fields

I have 1 table, that has records by date. I need to compare data from year 1 to year 2 (last year), but in some cases a few records in year 1 don't exist in year 2 and some in year 2 do not exist in year 1.
I only care about those that match. my structure is:
F_DATE F_TEXT1 F_TEXT2 F_NUMBER1 F_NUMBER2
2014-01-01 bob sue 19 12
2013-04-19 bob sue 12 11
2013-06-01 bob jane 5 6
2014-11-28 jane bob 4 4
2014-03-12 mike bob 8 1
so in the above example only care about the records that contain bob + sue.
I can identify records by concat(F_TEXT1,F_TEXT2) as f_compare to get bobsue i can then count on this field having count(*) > 1 but doing this doesn't work well because F_DATE is unique to the pairing, and F_NUMBER1 and F_NUMBER2 are fundamental for further processing.
I am joining to another table, which has a F_LABEL for the years and a Start Date (F_SDATE) and End Date (F_EDATE) column to provide a nice label for the years.
I am having difficulty getting my records into a query so i can the process them further.
I have tried to select everything i need - this query gives the extra records from year 1 and year 2. and i also select a F_DATE from T_SOMETABLE which is basically for config so this query can be updated to compare year 3 to 4 etc by changing F_SOMEVALUE.
SELECT F_LABEL, F_TEXT1, F_TEXT2, F_NUMBER1, F_NUMBER2, fix
FROM (
SELECT b.F_LABEL, a.F_TEXT1, a.F_TEXT2, a.F_HGOALS, a.F_AGOALS,
concat(F_HOME,'-',F_AWAY) as fix FROM all_records a, some_labels b
WHERE a.F_DATE > b.F_SDATE
AND a.F_DATE < b.F_EDATE
AND a.F_DATE > (SELECT F_DATE FROM T_SOMETABLE WHERE F_SOMEVALUE='1')
UNION ALL
SELECT F_LABEL, F_TEXT1, F_TEXT2, F_NUMBER1, F_NUMBER2,
concat(F_TEXT1,'-',F_AWAY) as fix
FROM all_records a, some_labels b
WHERE a.F_DATE > b.F_SDATE
AND a.F_DATE < b.F_EDATE
AND a.F_DATE > (SELECT F_DATE FROM T_SOMETABLE WHERE F_SOMEVALUE='2')
AND a.F_DATE < (SELECT F_DATE FROM T_SOMETABLE WHERE F_SOMEVALUE='1')
) z
ORDER BY F_TEXT1, F_TEXT2, F_LABEL
I can't get my head round select from year 2 where the concat (above) exists in year 1 and then selecting the F_LABEL, F_TEXT1, F_TEXT2, F_NUMBER1 and F_NUMBER2from both years into one table.
Can you help point me in the right direction?
Bonus points if this query can go into a VIEW (nested statements don't help here) so the query/table doesn't need to be recreated every time a new record is added.
The query needs to be perform well as the output would appear on a webpage.
It is difficult to give a precise answer but, in order to compare a year with the previous one, you need to join your table with itself (t1 and t2 below are two occurrences of yourtable), to form a query like this:
SELECT ...
FROM yourtable t1, yourtable t2
WHERE (t1.F_TEXT1 = t2.F_TEXT1
OR t1.F_TEXT2 = t2.F_TEXT2)
AND YEAR(t1.F_DATE) = YEAR(t2.F_DATE) - 1