Laravel Query get row based on 2 dependent column - mysql

Help me to query this.
I have a Laravel app where have a table that has 3 columns (quartal, year, value).
quartal
year
value
1
2019
3
2
2019
5
3
2019
5
4
2019
10
1
2020
7
2
2020
5
For example, I want to get the value from quartal 3 years 2019 to quartal 2 years 2020
how to make the query for this case?
for the year I can use between but for the quartal, It depends on the year.
here is my current query. but it does not work really well
DB::table($table)
->whereBetween('year',[$startYear, $endYear])
->whereBetween('quartal',[$startQuartal, $endQuartal])
->get();

What you need to do is you need to create a compound column from quartal and year and then query that.
DB::table($table)
->whereRaw("concat(year,quartal) >= ?", "$startYear$startQuartal")
->whereRaw("concat(year,quartal) <= ?", "$endYear$endQuartal")
->get();
You can refer this for the SQL query https://www.db-fiddle.com/f/4GRH5RKqQi2Fc6wVvxK8C2/0

you can do it with filter function also and it an easy way just like
$startYear = 2019
$endyear = 2020
$startQuartar = 3
$endQuartar = 2
DB::table($table)
->whereBetween('year',[$startYear, $endYear])
->get()->filter(function($query) use
($startYear,$endYear,$startQuartar,$endQuartar){
if($query->year == $startYear and $query->quartar < $startQuartar){
return false;
}
if($query->year == $endYear and $query->quartar > $endQuartar){
return false;
}
return true;
});

How about adding the quartal to the year?
DB::table($table)
->whereBetween(
DB::raw('year + (quartal - 1) / 4'),
[
$startYear + ($startQuartal - 1) / 4,
$endYear + ($endQuartal - 1) / 4
]
)
->get();

Related

How with mysql get the same as with php insert...on duplicate key update multiple rows?

For example have such table (named purchase_invoice_items)
Id
NameOfItem
PurchaseQuantity
PurchaseDate
SoldQuantity
1
x
2
2022-04-01
2
y
11
2022-04-01
3
z
8
2022-05-19
4
x
23
2022-08-19
5
x
15
2022-05-19
And i know that sum of sold quantity for NameOfItem x is 20. Sold 20 units of item x. I want to distribute the sold items between PurchaseQuantity using first-in-first-out method. Want to see table like this
Id
NameOfItem
PurchaseQuantity
PurchaseDate
SoldQuantity
1
x
2
2022-04-01
2
2
y
11
2022-04-01
3
z
8
2022-05-19
4
x
23
2022-08-19
3
5
x
15
2022-05-19
15
Using mysql two queries and php, i can do it in following way.
At first i select necessary data from mysql:
$sql_select_purchase_data = 'SELECT `IdPii`, `PurchasedQuantity`
FROM `purchase_invoice_items` WHERE `NameOfItem` = "x"
ORDER BY `PurchaseDate` ASC;';
Then create sql to update.
$sql_update_sold_quantity = 'INSERT INTO `purchase_invoice_items` (`IdPii`, `SoldQuantity`) VALUES ';
php code to continue creating sql
if( isset($arr_select_purchase_data) ){
$sum_of_sold_quantity = 20;
foreach( $arr_select_purchase_data as $one_arr_select_purchase_data ){
if( $sum_of_sold_quantity > 0 ){
$sql_update_sold_quantity .= '(?,?), ';
$data_update_sold_quantity[] = $one_arr_select_purchase_data['IdPii'];//For 'IdPii'
$data_update_sold_quantity[] = min( $one_arr_select_purchase_data['PurchasedQuantity'], $sum_of_sold_quantity);//For 'SoldQuantity'
$sum_of_sold_quantity = $sum_of_sold_quantity - min( $one_arr_select_purchase_data['PurchasedQuantity'], $sum_of_sold_quantity);
}//if( $sum_of_sold_quantity > 0 ){
else{ break; }
}//foreach(
$sql_update_sold_quantity = rtrim(trim($sql_update_sold_quantity), ','). ' ON DUPLICATE KEY UPDATE `SoldQuantity`= VALUES(`SoldQuantity`);';
But this is waste of resources (if i need to select-update many rows)? Two mysql queries and additionally php code.
Any ideas how can i get the same using only mysql (one mysql query; without php)?

Is there a convienient way to call a function multiple times without a loop?

I am currently making some code to randomly generate a set of random dates and assigning them to a matrix. I wish to randomly generate N amount of dates (days and months) and display them in a Nx2 matrix. My code is as follows
function dates = dategen(N)
month = randi(12);
if ismember(month,[1 3 5 7 8 10 12])
day = randi(31);
dates = [day, month];
elseif ismember(month,[4 6 9 11])
day = randi(30);
dates = [day, month];
else
day = randi(28);
dates = [day, month];
end
end
For example if I called on the function, as
output = dategen(3)
I would expect 3 dates in a 2x3 matrix. However, I am unsure how to do this. I believe I need to include N into the function somewhere but I'm not sure where or how.
Any help is greatly appreciated.
You can do it using logical indexing as follows:
function dates = dategen(N)
months = randi(12, 1, N);
days = NaN(size(months)); % preallocate
ind = ismember(months, [1 3 5 7 8 10 12]);
days(ind) = randi(31, 1, sum(ind));
ind = ismember(months, [4 6 9 11]);
days(ind) = randi(30, 1, sum(ind));
ind = ismember(months, 2);
days(ind) = randi(28, 1, sum(ind));
dates = [months; days];
end

Regression by year and companyID to save coefficients

I am trying to run regressions by companyID and year, and save the coefficients for each firm-year model as new variables in a new column right besides the other columns. There is an additional wrinkle‹ I have panel data for 1990-2010 and want to run each regression using t to t-4 only (I.e., for 2001, use only 1998-2001 years of data and i.e. for 1990 then only the data of 1990 and so on). I am new to using foreach loops and I found some prior coding on the web. I have tried to adapt it to my situation but two issues: anything.....
the output is staying blank
I have not figured out how to use the rolling four year data periods.
Here is the code I tried. Any suggestions would be much appreciated.
use paneldata.dta // the dataset I am working in
generate coeff . //empty variable for coefficient
foreach x of local levels {
forval z = 1990/2010
{
capture reg excess_returns excess_market
replace coeff = _b[fyear] & _b[CompanyID] if e(sample) }
}
So below is a short snapshot of what the data looks like;
CompanyID Re_Rf Rm-Rf Year
10 2 2 1990 
10 3 2 1991 
15 3 2 1991 
15 4 2 1992
15 5 2 1993 
21 4 2 1990 
21 4 2 1991 
34 3 1 1990 
34 3 1 1991
34 4 1 1992
34 2 1 1993  
34 3 1 1994
34 4 1 1995
34 2 1 1996   
 
Re_Rf = excess_returns 
Rm_Rf = excess_market 
I want to run the following regression: ​​​​​​​
reg excess_returns excess_market
There is a good discussion on Statalist, but I think this answer may be helpful for your learning about loops and how Stata syntax work.
the code I would use is as follows:
generate coeff = . //empty variable for coefficient
// put the values of gvkey into a local macro called levels
qui levelsof CompanyID, local(levels)
foreach co of local levels {
forval yr = 1994/2010 {
// run the regression with the condition that year is between yr
// and yr-3 (which is what you write in your example)
// and the CompanyID is the same as in the regression
qui reg Re_Rf Rm_Rf if fyear <= `yr' & fyear >= `yr'-3 & CompanyID== `co'
// now replace coeff equal to the coefficient on Rm_Rf with the same
// condiditions as above, but only for year yr
replace coeff = _b[Rm_Rf] if fyear == `yr' & CompanyID == `co'
}
}
This is a potentially dangerous thing to do if you do not have a balanced panel. If you are worried about this, there may be a way to deal with it using capture or changing the fyear loop to include something like:
levelsof fyear if CompanyID == `co', local(yr_level)
foreach yr of `yr_level' { ...

Datediff in MsAccess

I am stuck in one place.
I am using DateDiff in Ms Access it is giving me proper output, like
StartDate is 10-Sep-2016
EndDate is 15-Oct-2016
Total Days which I will get is 35
& months will i get is 1 Month
DateDiff('d',StartDate,EndDate)
**But I want output as 2 months if it is exeeded the 30 days.
if it is 61 days then 3 months & so on.
**IIFFF days diffrence is
29 Days then output should be 1 months
30 Days then output should be 1 months
32 Days then output should be 2 months
60 Days then output should be 2 months
62 Days then output should be 3 months**
Will that be possible in the DateDiff in MsAccess
or is there any other function available so that i can achieve the same output.**
You can do this using conditional logic. Perhaps something like this:
select iif(DateDiff('d', StartDate, EndDate) > 30,
DateDiff('d',StartDate,EndDate) & " days",
"2 months"
)
Your logic that anything exceeding 30 days is "2 months" seems strange. Normally, I think the logic would look like this:
select iif(DateDiff('d', StartDate, EndDate) > 30,
DateDiff('d', StartDate, EndDate) & " days",
DateDiff('m', StartDate, EndDate) & " months"
)
will this logic suffice to modify your SQL function?
Public Function FN_GET_MONTH(iDays As Long, Optional iDaysInMonth As Long = 30)
If (iDays / iDaysInMonth) > iDays \ iDaysInMonth Then
FN_GET_MONTH = (iDays \ iDaysInMonth) + 1
Else
FN_GET_MONTH = (iDays \ iDaysInMonth)
End If
End Function
?FN_GET_MONTH(29) = 1
?FN_GET_MONTH(31) = 2
?FN_GET_MONTH(60) = 2
?FN_GET_MONTH(80) = 3
?FN_GET_MONTH(91) = 4
you can have this public function and use it in your SQL code like
FN_GET_MONTH(DateDiff("d", StartDate, EndDate))
This query seems to give the results you seek:
SELECT
StartDate,
EndDate
numDays,
((numDays - 1) \ 30) + 1 AS numMonths
FROM
(
SELECT
StartDate,
EndDate,
DateDiff("d", StartDate, EndDate) AS numDays
FROM YourTable
)
It gives me
numDays numMonths
------- ---------
...
29 1
30 1
31 2
32 2
...
59 2
60 2
61 3
62 3
...
It seems like your minimum count of months for a positive count of days is 1, thus:
MonthCount = Sgn(DateDiff("d",StartDate,EndDate)) + DateDiff("m",StartDate,EndDate)
Edit
For a 30-day cut that will produce your example output, use this simple formula in your query:
MonthCount: (DateDiff("d",[StartDate],[EndDate])-1)\30+1

Mysql query to Get booking date periods, where bookings are more than a limit

I am developing a hotel room reservation system, There having only 5 rooms. I want to get the date periods if 5 or more bookings are already done.
Example:
+-------------------------------------------------------------+
|Booking_from_date | Booking_to_date | Number_of_booking_rooms|
+------------------+-----------------+------------------------+
| 2013-01-01 | 2013-01-10 | 3 |
+------------------+-----------------+------------------------+
| 2013-01-06 | 2013-01-15 | 2 |
---------------------------------------------------------------
(now there are total 5 room booked between 2013-01-06 to 2013-01-10, so i want to get this date period).
I tried using MySql, but not achieved yet. Is it possible to create a query like this?
There may a different simpler way, but what i can think of is to split the days and group it yo get sum.
select date_add(a.booking_from_date, interval col1 day),
sum(Number_of_booking_rooms) from
(select * from table1)a,
(select 0 col1 union all
select 1 col1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) b
where date_add(a.booking_from_date, interval col1 day) <= a.booking_to_date
group by date_add(a.booking_from_date, interval col1 day)
having sum(Number_of_booking_rooms) >= 5
http://sqlfiddle.com/#!2/87813/15
This is the result i got.
DATE SUM
January, 06 2013 00:00:00+0000 5
January, 07 2013 00:00:00+0000 5
January, 08 2013 00:00:00+0000 5
January, 09 2013 00:00:00+0000 5
January, 10 2013 00:00:00+0000 5
I guess this is not possible from just MySql query.But I have a solution by using loop. Please check this code, you will get the output which you want.
/* all active booking from mysql table. */
$this -> data['booked_dates'] = $this -> admin_model -> get_booked_dates_from();//(just a function to get all dates from table)
$array_index = 0;
$not_available_date_range = array();
//declaration
$not_available_date = array();
foreach ($this -> data['booked_dates'] as $key => $booked_date)// loop for each booking dates(each table row).
{
//flag to know package is to be blocked or not...
$block_status = 0;
$check_in_min_date = 0;
$check_out_max_date = 0;
$strDateFrom = $booked_date -> check_in_date;
$strDateTo = $booked_date -> check_out_date;
// Takes two dates, formatted in YYYY-MM-DD.
// Inclusive array of the dates between the from and to dates.
$aryRange = array();
$iDateFrom = strtotime($strDateFrom);
$iDateTo = strtotime($strDateTo);
if ($iDateTo >= $iDateFrom)//just a validation
{
array_push($aryRange, date('Y-m-d', $iDateFrom));
// first entry
$date_gap = 0;
while ($iDateFrom <= $iDateTo)//select each date...between check-in date and check-out date. one by one
{
$date_gap++;
$booking_count = 0;
for ($i = $key; $i <= (count($this -> data['booked_dates']) - 1); $i++)// loop for each booking. // loop have to reduce size. by using good logic..:)
{
$strDateFrom2 = strtotime($this -> data['booked_dates'][$i] -> check_in_date);
$strDateTo2 = strtotime($this -> data['booked_dates'][$i] -> check_out_date);
if (($iDateFrom >= $strDateFrom2) && ($iDateFrom <= $strDateTo2))
{
$booking_count = $booking_count + $this -> data['booked_dates'][$i] -> rooms;
}
}
if ($booking_count >= 5)// compare with maximum available rooms.
{
$block_status = 1;
//room should be blocked.
if ($check_in_min_date == 0)
{
$check_in_min_date = $iDateFrom;
$check_out_max_date = $iDateFrom;
//For if only one day is going to block.
}
else
{
$check_out_max_date = $iDateFrom;
//datefrom is incremented till the maximum booked date.
}
array_push($not_available_date, date('Y-m-d', $iDateFrom));
}
$iDateFrom += 86400;
// add 24 hours
}
}
else
{
echo 'Wrong input!';
}
if ($block_status)//if the room is blocked..
{
$not_available_date_range[$array_index]['check_in_date'] = date('Y-m-d', $check_in_min_date);
$not_available_date_range[$array_index]['check_out_date'] = date('Y-m-d', $check_out_max_date);
$array_index++;
}
}
$this -> data['blocked_dates'] = $not_available_date_range; // Now we have booking period where more than 5 bookings are done.