SQLalchemy merge two queries from same table - sqlalchemy

I need to query the total sales & guest for each Store for yesterday and the same day last year with the goal to be able to display in a table using jinja2.
I currently have two queries with a Union...
this_year = db.session.query(Sales.name,
func.sum(Sales.sales).label('total_sales'),
func.sum(Sales.guests).label('total_guests')
).filter(Sales.date >= start_day,
Sales.date <= end_day
).group_by(Sales.name)
last_year = db.session.query(Sales.name,
func.sum(Sales.sales).label('total_sales'),
func.sum(Sales.guests).label('total_guests')
).filter(Sales.date >= start_day_ly,
Sales.date <= end_day_ly
).group_by(Sales.name)
daily_table = this_year.union(last_year).all()
... but it gives me a list like so:
STORE 1, SALES, GUESTS
STORE 1, SALES_LY, GUESTS_LY
STORE 2, SALES, GUESTS
STORE 2, SALES_LY, GUESTS_LY
ETC,,
what i want is:
STORE 1, SALES, SALES_LY, GUESTS, GUESTS_LY
STORE 2, SALES, SALES_LY, GUESTS, GUESTS_LY
class Sales(db.Model):
__tablename__ = 'Sales'
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.String(64))
daypart = db.Column(db.String(64))
name = db.Column(db.String(64))
sales = db.Column(db.Integer)
guests = db.Column(db.Integer)
data:
|id|date|daypart|name|sales|guests|
|-----|-----|-----|-----|-----|-----|
|14896| 2021-11-22| Dinner| STORE 1| 11250.05| 288|
|14897| 2021-11-22| Lunch| STORE 1| 9250.25| 157|
|14898| 2021-11-22| Dinner| STORE 2| 5764.95| 169|
|14899| 2021-11-22| Lunch| STORE 2| 5856.25| 168|
|14900| 2021-11-22| Dinner| STORE 3| 9186.7| 320|
|14901| 2021-11-22| Lunch| STORE 3| 7521.0| 175|
This data goes back several years.
I looked at GROUP_BY, SUBQUERY, CASE, JOIN but have not found a solution that works.

you have to use with_entities, and use and_
this_year = db.session.query(Sales)
.filter(and_(Sales.date >= start_day, Sales.date <= end_day))
.with_entities(
Sales.name.label('name'),
func.sum(Sales.sales).label('total_sales'),
func.sum(Sales.guests).label('total_guests')
)
.group_by(Sales.name)
last_year = db.session.query(Sales)
.filter(and_(Sales.date >= start_day_ly,Sales.date <= end_day_ly))
.with_entities(
Sales.name.label('name'),
func.sum(Sales.sales).label('total_sales'),
func.sum(Sales.guests).label('total_guests')
)
.group_by(Sales.name)
daily_table = this_year.union(last_year).all()

Related

How to get average value from another average using MySQL

I have a Table that stores subjects. Each subject has Papers say Paper 1,2,3... and each paper has several tests that are undertaken like say TEST 1, 2, 3 ..like that.
Now what I want is to get the Average of TESTS undertaken in a particular paper which I have managed to get as below in Average Mark column;
I need help on how to get the average of the paper taken like an average of Paper 1 and Paper 2. to be put in paper_avarage column which should, in this case, be 32.5 but I'm getting 30.00 which is wrong
This is my code sofar which is giving me the average that is slightly wrong.
SELECT SubjectCode
, PaperNo
, TestNo
, MarkScored
, AverageMark
, (SELECT avg(AverageMark) As avMark
FROM test_results
WHERE AdminNo = 'SPS-20-O-0003'
AND SubjectCode = 112
AND StudyYear = 2020
AND StudyTerm = 1
AND StudyClass = 1) as paper_average
FROM test_results
WHERE AdminNo = 'SPS-20-O-0003'
AND SubjectCode = 112
AND StudyYear = 2020
AND StudyTerm = 1
AND StudyClass = 1
ORDER
BY PaperNo ASC
Thanks for your help in advance.
You want an average of averages, so replace your subquery with this:
(
SELECT AVG(t.avMark) AS avMark
FROM (
SELECT AVG(AverageMark) AS avMark
FROM test_results
WHERE AdminNo = 'SPS-20-O-0003'
AND SubjectCode = 112
AND StudyYear = 2020
AND StudyTerm = 1
AND StudyClass = 1
GROUP BY PaperNo
) AS t
) AS paper_average

what are the orders that took more than a day to deischarge, i have date of order and date of discharge

I started my career in data analysis and I have to use sql statements in day to day work. I am learning but need to also provide some quick answers. So I thought I will ask some questions in this group.
I would need help to write sql query in getting the orders that took more than one or tow days (based on rquirement) to discharge from the location.
Type of activity column represents 1,2,3,4
1-Order placed
2-Order discharged
Date is recorded in the column date in the corresponding row
Now i would like to call for all the orders that to took more than certain number of days 'n'
This is an example of the table how my table looks like.
Activities Table
|Order Nr| activity|date|
| 1 | 1 | date1| order placed
| 1 | 3 | date2| order approved
| 1 | 4 | date3| order packed
| 1 | 2 | date4| order discharged
Not exists is one method:
select a.*
from activities a
where a.activity = 'placed' and
not exists (select 1
from activities a2
where a2.activity = 'discharged' and
a2.ordernum = a.ordernum and
a2.date >= a.date and
a2.date <= a.date + interval 1 day
);
You get order placements with
select * from activities where activity = 1
and you can probably guess how to get order discharges :-)
So combine the two and keep only rows with too high a difference:
select p.order_nr, p.date as placed, d.date as discharged
from (select * from activities where activity = 1) p
join (select * from activities where activity = 2) d
on d.order_nr = p.order_nr and datediff(d.date, p.date) > 1;
You can get the same with an aggregation per order:
select
order_nr,
any_value(case when activity = 2 then date end) as placed,
any_value(case when activity = 1 then date end) as discharged
from activities
group by order_nr
having datediff(any_value(case when activity = 2 then date end),
any_value(case when activity = 1 then date end)) > 1;
In case you want to include open orders, you'd do almost the same. For orders without a discharged record it is possible that this will be entered today, so orders placed yesterday may still be fine whereas orders placed before are open too long already. So in case there is no dicharged record we want to pretend there is one with date = today.
Query #1:
select p.order_nr, p.date as placed, d.date as discharged
from (select * from activities where activity = 1) p
left join (select * from activities where activity = 2) d
on d.order_nr = p.order_nr and datediff(coalesce(d.date, curdate()), p.date) > 1;
Query #2:
select
order_nr,
any_value(case when activity = 2 then date end) as placed,
any_value(case when activity = 1 then date end) as discharged
from activities
group by order_nr
having datediff(any_value(case when activity = 2 then date end),
coalesce(any_value(case when activity = 1 then date end), curdate())) > 1;

Finding rows that satisfy some date constraints existing in a table

I have a table like this
CustomerId serviceID from To
id1 serv1 '2016-01-01 07:22:33' '2016-01-05 07:22:33'
id1 serv1 '2016-02-21 08:30:10' '2016-02-27 08:30:10'
id1 serv2 '2016-02-15 18:30:10' '2016-02-15 19:30:10'
id1 serv3 '2016-02-19 18:30:10' '2016-02-19 19:30:10'
id1 serv4 '2017-01-01 08:30:10' '2017-01-01 09:00:10'
where:
serv1 = "in Dog Hospital"
serv2 = "X ray service"
serv3 = "Recovering meal"
For a report, I need to find all those rows that imply the "dog" took that service while in the "Dog hospital" i.e. those services taken during the dates the dog was at the "dog hospital".
Any suggestions, please?
You could check for the rows than fall in serv1's period like this:
select *
from t a
where exists (
select 1
from t b
where serviceId = 'serv1'
and a.`from` >= b.`from`
and a.`to` <= b.`to`
);
Demo # SQLFiddle

sql for product price update from exchange rate from multiple table

I have two tables "curreny" and "product_price". Some goods imported so we need to keep their prices updated as the currency rates changes.
"curreny" table keeps updated currency rates for various currencies.
"product_price" table keeps fields "main_price","foreign_price","currency_iso","base_currency"
Curreny Table (daily updated)
c_iso rate
---------------
USD 3.0257
EUR 3.3547
Product_price Table
id def main_price foreign_price currency_iso base_currency
1 Product1 30.2570 10 USD 1
2 Product2 50 14.904 EUR 0
3 Product3 67.094 20 EUR 1
I need to update all product price according to base_currency.
For example for product1 base_currency is 1, which means USD is the real price of the product. Thus according to the new rates on table "currency" the foeign_price of the product should multiply by USD rate.
main_price = foreign_price*rate
for product2 base_currency is 0, which means main_price is the real price of the product. Thus according to the new rates on table "currency" the foeign_price of the product should be dived by USD rate.
foreign_price = main_price/rate.
After trying multiple settings of if/case statements, i found a working code:
update product_price as p
inner join currency as c on(c.c_iso = p.currency_iso)
set p.main_price = case p.base_currency when true then
p.foreign_price*c.rate when false then p.main_price
end,
p.foreign_price = case p.base_currency when false then
p.main_price/c.rate when true then p.foreign_price
end;
I think this is the update you need:
update product_price p join
currency c
on c.c_iso = p.currency_iso
set main_price = (case when p.base_currency = 1
then p.foreign_price * c.rate
else p.foreign_price / c.rate
end)
where p.base_currency in (0, 1);
EDIT:
Based on the comment:
update product_price p join
currency c
on c.c_iso = p.currency_iso
set main_price = (case when p.base_currency = 1
then p.foreign_price * c.rate
else main_price
end),
foreign_price = (case when p.base_currency = 0
then p.foreign_price / c.rate
else foreign_price
end)
where p.base_currency in (0, 1);

Attendance Report in MySql

I want to write a query to generate attendance report of employee. First I will tell you how the presence of employee is stored in my database.
I have following tables.
Employee Table with Columns
emp_id emp_Name Joining_Date
1 john 11-01-2012
2 Scott 12-01-2012
Holiday Table
Holiday_Name Date
Chrismas 25-12-2012
Dushera 08-03-2012
Independance Day 15-08-2012
Leave Table
Subject from_Date to_Date Emp_Id status
PL 02-01-2012 04-01-2012 1 Approved
CL 11-01-2012 12-01-2012 2 Declined
Doctor Table
Subject Call_Date call_Done_By(emp_id)
Call 15-01-2012 1
CA 21-02-2012 2
Chemist Table
Subject Call_Date call_Done_By(emp_id)
Chemist 1-02-2012 2
Texo 21-03-2012 1
If employee is visited to doctor or chemist,that particular date is stored in that particular doctor or chemist table with employee_id
Now person will select year and month and he should be able to get attendance report in following format
Example : suppose user selects year as '2011' and month as 'Dec' then output should be
Employee year Month 1 2 3 4 5 6 7....
John 2011 Nov Y Y Y Y Y L S....
Scott 2011 Nov Y Y L M Y L S
here in output 1,2,3.... are days from 0-30 for a month which we can write using 'case'
Consider if employee is present on day show its status as 'Y' else L else
if he gone to any customer like doctor,chemist,then replace it with 'S'.
So how should I write a query to achieve this output??
any suggestions will be helpful for me....
Here is a long way that should work as expected:
SELECT
Employee.emp_Name,
'2011' AS `Year`,
'Dec' AS `Month`,
CASE (
IF(
DATE('1-12-2011') < DATE(Employee.Joining_Date)),
'0' --Not joined yet
IF (
(SELECT COUNT(*) FROM Holiday WHERE DATE('1-12-2011') = DATE(Holiday.date)) = 1,
'1', --National Holiday
IF (
(SELECT COUNT(*) FROM Leave WHERE DATE('1-12-2011') > DATE(Leave.to_Date) AND DATE('1-12-2011') < DATE(Leave.from_Date) AND Leave.Emp_Id = Employee.emp_id) = 1,
'2', --On Leave
IF(
(SELECT COUNT(*) FROM Doctor WHERE DATE('1-12-2011') > DATE(Doctor.Call_Date) AND Doctor.call_Done_By = Employee.emp_id) = 1 OR
(SELECT COUNT(*) FROM Chemist WHERE DATE('1-12-2011') > DATE(Chemist.Call_Date) AND Chemist.call_Done_By = Employee.emp_id) = 1,
'3' --Visit Doctor or Chemist
'4' --Employee was at work
)
)
)
)
)
WHEN 0 THEN 'N/A' --Not joined yet
WHEN 1 THEN 'L' --National Holiday
WHEN 2 THEN 'L' --On Leave
WHEN 3 THEN 'S' --Visit Doctor or Chemist
ELSE 'Y' --Employee was at work
END AS `1`, --first day of month
... AS `2`, --repeat for second day of the month till max day of current month replace '1-12-2011' with each different day of month
...
... AS `30`
FROM
Employee
My suggestion is to create a view that does the if statement for each employee that way your code will be easier to maintain. Please keep in mind that this is pseudo code that might need some some changing to run.
Hope this helps.