Issue with Group by Mysql query - mysql

I have been stumped by this MySQL query for the past few day,and seeking some expert help before i throw my PC out of the window, lol.
I have a table that consists of invoices; invoice number column name is Trans_ref$$ (primary key).
each invoice is linked to a job , column name is OPSREF$$_ORIGINAL (foreign key).
Both columns are in the same table.
a job can have multiple invoices.
Now the issue is i'm trying to create a query that will identify the FIRST invoice in sequence (Trans_ref$$) in a job group and if that FIRST invoice is with a date range then output the job (OPSREF$$_ORIGINAL).
The invoice number is sequential and a unique number.
I have tried using a case / min group by statement to identify the first invoice in a job group within a data range, however this query is not isolating the first invoice within a date range, instead it is just outputting 1st or 2nd or 3rd positioned invoice in job group that matches the date range….I hope this makes sense .
I have included a screen shot of the table and the desired output, and would appreciate if someone
Hello
I have been stumped by this query for the past day,and seeking some expert help before i throw my PC out of the window, lol.
I have a table that consists of invoices; invoice number column name is Trans_ref$$ (primary key).
each invoice is linked to a job , column name is OPSREF$$_ORIGINAL (foreign key).
Both columns are in the same table.
a job can have multiple invoice.
Now the issue is i'm trying to create a query that will identify the FIRST invoice (Trans_ref$$) in a job group and if that FIRST invoice is with a date range then output the job (OPSREF$$_ORIGINAL).
The invoice number in a sequential / unique number.
I have tried using a case / min group by statement to identify the first invoice in a job group within a data range, however this query is not isolating the first invoice within a date range, instead it is just outputting 1st or 2nd or 3rd positioned invoice in job group that matches the date range….I hope this makes sense .
Here the code I have used
SET #START_DATE := "2014-08-27";
SET #END_DATE:= "2014-09-02";
select
count(TRANS_REF$$),
Case
when min(TRANS_REF$$) and INVOICE_DATE_D8>=#START_DATE and INVOICE_DATE_D8<=#END_DATE
then OPSREF$$_ORIGINAL
end as OPSREF$$_ORIGINAL
from INVOICE_HEADER_UNS
where
TRANSACTION_STATUS = 9
and Tmode$$="06"
group by OPSREF$$_ORIGINAL
So to breakdown the process in stages.....
1. grab each OPSREF$$_ORIGINAL group within the INVOICE_HEADER_UNS table
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL
---------------------------------------------
| 1 | 26/08/2014 | ABC |
| 2 | 02/08/2014 | ABC |
---------------------------------------------
| 3 | 28/08/2014 | DDD |
| 4 | 09/09/2014 | DDD |
---------------------------------------------
| 5 | 01/01/2013 | JJJ |
| 6 | 21/12/2013 | JJJ |
---------------------------------------------
| 7 | 01/09/2014 | LLL |
---------------------------------------------
2. within each OPSREF$$_ORIGINAL group, look at TRANS_REF$$ sequence and find the first TRANS_REF$$ in that OPSREF$$_ORIGINAL group.
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL Is First inv?
--------------------------------------------------------
| 1 | 26/08/2014 | ABC | yes
| 2 | 02/08/2014 | ABC | no
--------------------------------------------------------
| 3 | 28/08/2014 | DDD | yes
| 4 | 09/09/2014 | DDD | no
---------------------------------------------------------
| 5 | 01/01/2013 | JJJ | yes
| 6 | 21/12/2013 | JJJ | no
---------------------------------------------------------
| 7 | 01/09/2014 | LLL | yes
---------------------------------------------------------
3. Look at the the FIRST INVOICE in the ordered sequence for the group and check the INVOICE_DATE_D8 for that row and see if it is within date range ( 27/08/2014 - 02/09/2014 )
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
TRANS_REF$$ INVOICE_DATE_D8 OPSREF$$_ORIGINAL Is First inv? In date range?
--------------------------------------------------------
| 1 | 26/08/2014 | ABC | yes no
--------------------------------------------------------
| 3 | 28/08/2014 | DDD | yes yes
---------------------------------------------------------
| 5 | 01/01/2013 | JJJ | yes no
---------------------------------------------------------
| 7 | 01/09/2014 | LLL | yes yes
4. Does each row meet the criteria (i.e. first invoice and in date - YES + YES) ? if correct then output the distinct OPSREF$$_ORIGINAL for that row
Here is the desired output from the example above
Date Range - Start 27/08/2014 - End 02/09/2014
TABLE - INVOICE_HEADER_UNS
OPSREF$$_ORIGINAL Is First inv? In date range?
------------------------
| DDD | yes yes
------------------------
| LLL | yes yes
------------------------
Based on this, can you suggest how I can do this? thanks
Here is the Table definition;
tablename - INVOICE_HEADER_UNS
**TRANS_REF$$** char(7) - Primary key
INVOICE_DATE_D8 datatime
OPSREF$$_ORIGINAL char(11)
16/09/2014 - Here is the query that appears to be returning the correct results using DRapp syntax, however the query needs to be optimised somehow as when I bolt this onto the bigger query as a sub query it causes my MYSQL workbench to crash. After doing an explain on the query , it appears to be indexing through 28,309 rows. How can i optimise this query without impacting the results?
I have tried adding the below to the where clause however it adds more records to the overall query output, but indexes 979 rows instead/
set #START_DATE := '2014-08-27';
set #END_DATE := '2014-09-02';
SELECT
if (min(ihu.INVOICE_DATE_D8)>= #START_DATE and min(ihu.INVOICE_DATE_D8)<= #END_DATE ihu.opsref$$_original, null) as firstinvoiceindaterange
from
INVOICE_HEADER_UNS ihu
join CONSIGNMENT_ALL_HEADER_UNS on ihu.OUR_REF = CONSIGNMENT_ALL_HEADER_UNS.OPSREF$$
where ihu.TRANSACTION_STATUS = 9 and ihu.Tmode$$="06"
AND CONSIGNMENT_ALL_HEADER_UNS.CONS_TYPE$$ <>'7'
AND CONSIGNMENT_ALL_HEADER_UNS.CONS_TYPE$$ in ('3' , '5', '9')
#and ihu.INVOICE_DATE_D8>= #START_DATE and ihu.INVOICE_DATE_D8<= #END_DATE
group by
ihu.opsref$$_original

I think this should help you out, and you can tweak it as needed.
SELECT
ihu.opsref$$_original,
MIN( ihu.TRANS_REF$$ ) MinRefForEntireJob,
MIN( ihu.INVOICE_DATE_D8 ) MinDateForEntireJob,
MIN( IF( ihu.INVOICE_DATE_D8 >= #START_DATE
AND ihu.INVOICE_DATE_D8 <= #END_DATE, ihu.TRANS_REF$$, null )) as MinRefWithinDateRange,
MIN( IF( ihu.INVOICE_DATE_D8 >= #START_DATE
AND ihu.INVOICE_DATE_D8 <= #END_DATE, ihu.INVOICE_DATE_D8, null )) as MinDateWithinDateRange
from
INVOICE_HEADER_UNS ihu,
( select #START_DATE := '2014-08-27',
#END_DATE := '2014-09-02' ) sqlvars
group by
ihu.opsref$$_original
Once you see these results, then you SHOULD be able to just add a HAVING clause something like
HAVING
MinDateWithinDateRange IS NOT NULL

Related

Need an aggregate MySQL select that iterates virtually across date ranges and returns bills

I have a MySQL table named rbsess with columns RBSessID (key), ClientID (int), RBUnitID (int), RentAmt (fixed-point int), RBSessStart (DateTime), and PrevID (int, references to RBSessID).
It's not transactional or linked. What it does track when a client was moved into a room and what the rent at the time of move in was. The query to find what the rent was for a particular client on a particular date is:
SET #DT='Desired date/time'
SET #ClientID=Desired client id
SELECT a.RBSessID
, a.ClientID
, a.RBUnitID
, a.RentAmt
, a.RBSessStart
, b.RBSessStart AS RBSessEnd
, a.PrevID
FROM rbsess a
LEFT
JOIN rbsess b
ON b.PrevID=a.RBSessID
WHERE a.ClientID=#ClientID
AND (a.RBSessStart<=#DT OR a.RBSessStart IS NULL)
AND (b.RBSessStart>#DT OR b.RBSessStart IS NULL);
This will output something like:
+----------+----------+----------+---------+---------------------+-----------+--------+
| RBSessID | ClientID | RBUnitID | RentAmt | RBSessStart | RBSessEnd | PrevID |
+----------+----------+----------+---------+---------------------+-----------+--------+
| 2 | 4 | 1 | 57500 | 2020-11-22 00:00:00 | NULL | 1 |
+----------+----------+----------+---------+---------------------+-----------+--------+
I also have
SELECT * FROM rbsess WHERE rbsess.ClientID=#ClientID AND rbsess.PrevID IS NULL; //for finding the first move in date
SELECT TIMESTAMPDIFF(DAY,#DT,LAST_DAY(#DT)) AS CountDays; //for finding the number of days until the end of the month
SELECT DAY(LAST_DAY(#DT)) AS MaxDays; //for finding the number of days in the month
SELECT (TIMESTAMPDIFF(DAY,#DT,LAST_DAY(#DT))+1)/DAY(LAST_DAY(#DT)) AS ProRateRatio; //for finding the ratio to calculate the pro-rated rent for the move-in month
SELECT ROUND(40000*(SELECT (TIMESTAMPDIFF(DAY,#DT,LAST_DAY(#DT))+1)/DAY(LAST_DAY(#DT)) AS ProRateRatio)) AS ProRatedRent; //for finding a pro-rated rent amount based on a rent amount.
I'm having trouble putting all of these together to form a single query that can output pro-rated and full rent amounts based on a start date and an optional end date all rent owed amounts in a single statement for each month in the period. I can add a payments table received and integrate it afterwards, just having a hard time with this seemingly simple real-world concept in a MySQL query. I'm using php with a MySQL back end. Temporary tables as intermediary queries are more than acceptable.
Even a nudge would be helpful. I'm not super-experienced with MySQL queries, just your basic CREATE, SELECT, INSERT, DROP, and UPDATE.
Examples as requested by GMB:
//Example data in rbsess table:
+----------+----------+----------+---------+---------------------+--------+
| RBSessID | ClientID | RBUnitID | RentAmt | RBSessStart | PrevID |
+----------+----------+----------+---------+---------------------+--------+
| 1 | 4 | 1 | 40000 | 2020-10-22 00:00:00 | NULL |
| 2 | 4 | 1 | 57500 | 2020-11-22 00:00:00 | 1 |
| 3 | 2 | 5 | 40000 | 2020-11-29 00:00:00 | NULL |
+----------+----------+----------+---------+---------------------+--------+
Expected results would be a list of the rent amounts owed for every month, including pro-rated amounts for partial occupancy in a month, from a date range of months. For example for the example data above for a date range spanning all of the year of 2020 from client with ClientID=4 the query would produce an amount for each month within the range similar to:
Month | Amt
2020-10-1 | 12903
2020-11-1 | 45834
2020-12-1 | 57500

Inserting random data from a list

These are my table columns:
ID || Date || Description || Priority
My goal is to insert random test data of 2000 rows with date ranging between (7/1/2019 - 7/1/2020) and randomize the priority from list (High, Medium, Low).
I know how to insert random numbers but I am stuck with the date and the priority fields.
If I need to write code, any pointers on how do I do it?
Just want to be clear - I have issue with randomizing and inserting from a given list
CREATE TABLE mytable (
id SERIAL PRIMARY KEY,
date DATE NOT NULL,
description TEXT,
priority ENUM('High','Medium','Low') NOT NULL
);
INSERT INTO mytable (date, priority)
SELECT '2019-07-01' + INTERVAL FLOOR(RAND()*365) DAY,
ELT(1+FLOOR(RAND()*3), 'High', 'Medium', 'Low')
FROM DUAL;
The fake table DUAL is a special keyword. You can select from it, and it always returns exactly one row. But it has no real columns with data, so you can only select expressions.
Do this INSERT a few times and you get:
mysql> select * from mytable;
+----+------------+-------------+----------+
| id | date | description | priority |
+----+------------+-------------+----------+
| 1 | 2019-10-20 | NULL | Medium |
| 2 | 2020-05-17 | NULL | High |
| 3 | 2020-06-25 | NULL | Low |
| 4 | 2020-05-06 | NULL | Medium |
| 5 | 2019-09-30 | NULL | High |
| 6 | 2019-08-06 | NULL | Low |
| 7 | 2020-02-21 | NULL | High |
| 8 | 2019-11-10 | NULL | High |
| 9 | 2019-07-30 | NULL | High |
+----+------------+-------------+----------+
Here's a trick to use the number of rows in the table itself to insert the same number of rows, basically doubling the number of rows:
INSERT INTO mytable (date, priority)
SELECT '2019-07-01' + INTERVAL FLOOR(RAND()*365) DAY,
ELT(1+FLOOR(RAND()*3), 'High', 'Medium', 'Low')
FROM mytable;
Just changing FROM DUAL to FROM mytable I change from selecting one row, to selecting the current number of rows from the table. But the values I insert are still random expressions, not the values already in those rows. So I get new rows with new random values.
Then repeat this INSERT as many times as you want to double the number of rows.
Read also about the ELT() function.
You seem to be looking for something like this. A basic random sample is:
select t.*
from t
where date >= '2019-07-01' and date < '2020-07-01'
order by random()
fetch first 2000 rows only;
Of course, the function for random() varies by database, as does the logic for limiting rows. This should get about the same distribution of priorities as in the original data.
If you want the rows to come by priority first, then use:
select t.*
from t
where date >= '2019-07-01' and date < '2020-07-01'
order by (case when priority = 'High' then 1 when priority = 'Medium' then 2 else 3 end),
random()
fetch first 2000 rows only;

Select a row for every date in the table, no matter the data

I have the following 3 tables in my database:
noobs
id
name
img_url
associations_id
noobs_has_points
noobs_id
points_id
points
id
amount
create_time (as UNIX timestamp)
I want to get a result for every day (such as FROM_UNIXTIME(points.create_time,'%Y-%m-%d')). And in that result I want the noobs.id and his amount of points so SUM(points.amount). So whether a noob has actually scored points on that day doesn't matter, if he did not I would want a row with 0 in there as the amount, so that for every day I get to see how many points each noob scored.
However, I have no idea how to get this result. I have tried some things with left/right (or unioned) joins but I don't get the result I want. Can anyone help me with this?
Example results:
day | points.amount | noobs.id
2015-04-11 | 3 | 1
2015-04-11 | 0 | 2 (no points scored, no entry in database)
2015-04-12 | 0 | 1 (no points scored, no entry in database)
2015-04-12 | 1 | 2
Some sample data from the three tables:
Noobs
id | name | img_url | associations_id
1 | Rien | NULL | 1
2 | Peter| NULL | 1
noobs_has_points
noobs_id | points_id
1 | 1
2 | 3
points
id | amount | create_time
1 | 3 | 1428779292
2 | 1 | 1428805351
Because there may be no dara for a given day for a given noob, you need a way to generate date values. Unfortunately, mysql doesn't have a built-in way to do this. You can code a range into the query with a series if unions as a subquery, but it's ugly and not scalable.
I recommend creating a table to hold date values:
create table dates(_date date not null primary key);
And populating it with lots of dates (say everything from 1970-2020).
Then you can code:
select _date day, sum(p.amount) total, n.id
from dates d
cross join noobs n
left join noobs_has_points np on np.noob_id = n.id
left join points p on p.id = np.points_id
and date(p.create_time) = _date
where _date between ? and ?
group by 1, 3
The cross join gives every noob a result for every date in the specified range, while to left joins ensure a zero for days without points for the noob.

Auto Increment mysql trigger

How create a Auto increment field based on this example:
I have this table, with "AF" field, in that format: SN.MM.YYYY
The "SN" = AI number based on last insert, MM= Atual Month, YYYY = Atual Year.
| ID | AF |
____________________
| 1 | 01.10.2013 |
| 2 | 02.10.2013 |
So, when changes the month or year, the trigger must set "AF" field that way:
Ex.: Month changes to November(Reset SN to 01).
| 3 | 01.11.2013 |
| 4 | 02.11.2013 |
The same thing when year changes(Reset SN to 01):
| 5 | 01.01.2014 |
| 6 | 02.01.2014 |
| 7 | 03.01.2014 |
Anyone know's how set that trigger?
Obs: There may be more than one record in one day, so, day is not important.
Sorry for the bad english
Thanks guys!
Technically you can do something like this
CREATE TRIGGER tg_bi_table1
BEFORE INSERT ON table1
FOR EACH ROW
SET NEW.af = CONCAT(
LPAD(COALESCE(
(SELECT MAX(LEFT(af, 2))
FROM table1
WHERE af LIKE DATE_FORMAT(CURDATE(), '__.%m.%Y')), 0) + 1, 2, '0'),
DATE_FORMAT(CURDATE(), '.%m.%Y'));
Here is SQLFiddle demo
Note: This approach (creating your own ago_increment values with such a pattern) has two major drawbacks:
Under heavy concurrent access different connections may obtain the same AF number
Because of your particular AF pattern (SN comes first) using an index is impossible therefore you'll end up always getting a full scan

MySQL - subtracting row 2 from row 1 where column is the same

I am creating a temp table which currently pulls values in for today and yesterday. The temp table looks like this:
|DateTime |company 1 |company 2 |
+------------------------+----------------------+
|2013-10-03 14:40:00 | 182475 | 110271 |
|2013-10-02 14:10:00 | 182086 | 110261 |
What I need to do is select the values from today, as well as the difference between today and yesterday. Based on the above data, the output would look like this:
Company 1 | Company 1 difference | Company 2| Company 2 difference
+-----------+----------------------+----------+--------------------------
182475 | 389 | 110271 | 10
I can't figure out how to specify which row to subtract from which, since there is no auto-increment field and the DateTime field will be slightly different each day.
Thanks for the help!
SELECT today.DateTime as DateTime
, today.Company1, today.Company1 - COALESCE(yesterday.Company1,0) AS Company1Diff
, today.Company2, today.Company2 - COALESCE(yesterday.Company2,0) AS Company2Diff
FROM tbl AS today
LEFT JOIN tbl AS yesterday
ON DATEDIFF(today.DateTime, yesterday.DateTime) = 1
ORDER BY today.DateTime ASC