MySQL stored procedure architecture - mysql

I am working on a piece of code that runs a query against MySQL. The syntax is below:
SELECT `order`.`id` AS `id`,
IFNULL(product.main_name, product.name) AS `variant`,
`product`.`id` AS `product_id`,
`product`.`cost` AS `cost`,
ROUND(SUM(order.price), 2) AS `price`,
SUM(order.quantity) AS `quantity`,
`product`.`sku` AS `sku`,
ROUND(order.price/order.quantity, 2) AS `avg_price`,
`product`.`quantity` AS `qty_at_hand`,
`order`.`fulfillment_channel` AS `fulfillment_channel`,
0 as `returns`
FROM `order`
LEFT JOIN `product` ON product.sku = order.sku
WHERE (`order`.`account_id`=1)
AND (`order`.`item_status`<>'Cancelled')
AND (`order`.`purchase_date` >= $start_date)
AND (`order`.`purchase_date` <= $end_date)
GROUP BY `order`.`sku`
My order table:
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| account_id | int(11) | NO | MUL | NULL | |
| merchant_order_id | varchar(255) | NO | | NULL | |
| purchase_date | datetime | NO | | NULL | |
| updated_date | datetime | NO | | NULL | |
| order_status | varchar(255) | NO | | NULL | |
| product_name | varchar(255) | NO | | NULL | |
| sku | varchar(255) | NO | MUL | NULL | |
| item_status | varchar(255) | NO | | NULL | |
| quantity | int(11) | NO | | NULL | |
| currency | varchar(10) | NO | | NULL | |
| price | decimal(9,2) | YES | | 0.00 | |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |
| fulfillment_channel | varchar(255) | YES | | NULL | |
My product table:
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| account_id | int(11) | NO | MUL | NULL | |
| name | varchar(255) | NO | | NULL | |
| main_name | varchar(255) | YES | | NULL | |
| description | text | YES | | NULL | |
| listing_id | varchar(255) | YES | | NULL | |
| sku | varchar(255) | NO | MUL | NULL | |
| open_date | datetime | YES | | NULL | |
| price | decimal(9,2) | NO | | 0.00 | |
| quantity | int(11) | YES | | NULL | |
| supplier_id | int(11) | YES | | NULL | |
| active | tinyint(1) | NO | | 1 | |
| supplier_sku | varchar(255) | YES | | NULL | |
| upc | varchar(255) | YES | | NULL | |
| tag | varchar(255) | YES | | NULL | |
| cost | decimal(9,2) | NO | | 0.00 | |
| ean | int(11) | YES | | NULL | |
| min_order_qty | int(11) | YES | | NULL | |
The query is working fine. However, I would like to replace the QUERY with a stored procedure - a function would take three (3) arguments: start_date, end_date and a boolean flag called full.
I would like the full to be interpreted in a the following way:
- if full is set to false, return the same results query returns now;
- if full is set to true, return a row for each entry in the products table with quantity, price and average price returned as 0, if where fails merged with the query results - a row for each product, whether where were orders in the set period or not.
I was thinking about making a temporary table, then running an update on it and dumping the table after the data has been returned?
What would be the correct way to approach this?
Thank you

Add a UNION to this query and have it do an LEFT OUTER JOIN from Products to Orders table and in the WHERE clause check for full = true and only look for rows with Order.id is NULL. Union would only bring rows when full = true.
Prepare data in SELECT as you specified.

Related

sql query count number of record in a join

I have to extract the list of POS(point of service) from pos table, based on location name:
POS
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| address | varchar(200) | YES | | NULL | |
| country | varchar(45) | YES | | NULL | |
| location | varchar(100) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
so, it would be something like :
select *
from pos
where location = 'new york';
Now , from the table mission i have to count the number of POS (pos_name), and if this number is greater than a number coming from a subquery, pos register should not be shown.
mission
| id | int(11) | NO | PRI | NULL | auto_increment |
| team_name | varchar(45) | YES | | NULL | |
| team_photo | varchar(255) | YES | | NULL | |
| pos_name | varchar(45) | YES | | NULL | |
| pos_id | int(11) | YES | | NULL | |
| product_category | varchar(45) | YES | | NULL | |
| product_platform | varchar(45) | YES | | NULL | |
| created_by | int(11) | YES | | NULL | |
| judged_by | int(11) | YES | | NULL | |
| selfie_photo | varchar(255) | YES | | NULL | |
| like_score | int(11) | YES | | NULL | |
| star_score | float | YES | | NULL | |
| feedback_recommendation | varchar(2000) | YES | | NULL | |
| created_at | timestamp | YES | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | YES | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| sent_date | timestamp | YES | | NULL | |
| locked | tinyint(1) | YES | | 0 | |
+-------------------------+---------------+------+-----+-------------------+-----------------------------+
so until now i developed following sql query :
SELECT p.id,p.name
FROM missions m join pos p
on m.pos_id = p.id
where p.location = 'new york'
having count(m.pos_name) < (select count(*)
from product_category);
but this part of code
having count(m.pos_name)
should be something like that :
select count(*)
from missions
group by pos_name;
because without group by statement i will recieve a sum of all pos, but i need to check it for a group, not for all of them.
Any suggestion ?
I thing you need the data with group by with condition then try this
SELECT p.id,p.name
FROM missions m join pos p
on m.pos_id = p.id
where p.location = 'new york'
group by m.pos_name
having count(m.pos_name) < (select count(*)
from product_category);
Just add group by m.pos_name it will work.
You can check Here

How to add a date selection in sql

I want to be able to select classes that are starting on or after the next two hours up until the end of tomorrow's date but I am not sure of how to do this.
My current sql is below, I have only been able to select classes that are after today's date and after the current time. I want to be able to only return classes that start two hours after now up until the end of tomorrows date.
I am able to pass the current date and current time into my node.js function that will construct the sql statement. Also, I have access to moment.js library if I need to use this I could.
Any help would be appreciated.
SELECT DISTINCT b.name
, a.time
FROM class a
Inner join (SELECT class_id, count(clientid)
FROM bookings
GROUP BY class_id
HAVING count(clientid) < 10) as openClasses on
a.class_id = openClasses.class_id
JOIN class_detail b
ON a.class_id = b.id
JOIN branch c
ON a.branch_id = c.id
WHERE c.level <= ( SELECT d.level
FROM client d
WHERE d.facebook_id = 'xxxxxx'
)
AND a.date = '2016-08-17'
AND a.time >= '13.00.00';
My tables are as follows:
BOOKINGS
+-----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id | bigint(20) | NO | PRI | NULL | |
| CLIENT_ID | int(11) | NO | | NULL | |
| CLASS_ID | int(11) | NO | | NULL | |
| STATUS | varchar(10) | NO | | NULL | |
+-----------+-------------+------+-----+---------+-------+
mysql> show fields from BRANCH;
+---------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-------------+------+-----+---------+----------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| NAME | char(50) | NO | | NULL | |
| CONTACT_NO | char(50) | YES | | NULL | |
| MAP_IMG_PATH | char(200) | YES | | NULL | |
| ADDRESS | char(200) | YES | | NULL | |
| LEVEL | int(2) | NO | | NULL | |
| LOCATION | int(10) | YES | | NULL | |
| SECTOR_NAME | varchar(45) | YES | | NULL | |
| SECTOR_MAP_IMG_PATH | char(200) | YES | | NULL | |
+---------------------+-------------+------+-----+---------+----------------+
mysql> show fields from CLIENT;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| id | int(10) | NO | PRI | NULL | |
| NAME | char(50) | NO | | NULL | |
| DOB | int(8) | NO | | NULL | |
| LOCAL_BRANCH | int(10) | YES | | NULL | |
| FACEBOOK_ID | char(50) | NO | | NULL | |
| START_DATE | int(8) | NO | | NULL | |
| EMAIL | char(50) | YES | | NULL | |
| PIN | int(4) | YES | | NULL | |
| END_DATE | int(8) | NO | | NULL | |
| LEVEL | int(2) | YES | | NULL | |
| TEL | varchar(20) | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+
mysql> show fields from CLASS_DETAIL;
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id | int(10) | NO | PRI | NULL | |
| NAME | char(50) | NO | | NULL | |
| DESCRIPTION | char(200) | NO | | NULL | |
| CATEGORY | varchar(4) | YES | | NULL | |
| ACHIEVE_TYPE | char(200) | YES | | NULL | |
| IMG_M | varchar(200) | YES | | NULL | |
| IMG_F | varchar(200) | YES | | NULL | |
+--------------+--------------+------+-----+---------+-------+
mysql> show fields from CLASS;
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| CLASS_ID | int(10) | YES | | NULL | |
| BRANCH_ID | int(10) | NO | | NULL | |
| DURATION | int(3) | YES | | NULL | |
| DATE | date | NO | | NULL | |
| TIME | time | NO | | NULL | |
| STATUS | char(1) | NO | | NULL | |
+-----------+---------+------+-----+---------+----------------+
7 rows in set (0.11 sec)

Need a SQL command for these tables

I have the following 2 tables (student and attendance):
mysql> describe student;
+----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+---------+----------------+
| student_id | int(11) | NO | PRI | NULL | auto_increment |
| student_email | varchar(255) | YES | | NULL | |
| student_phone_number | varchar(255) | YES | | NULL | |
| parent_first_name | varchar(255) | NO | | NULL | |
| parent_last_name | varchar(255) | NO | | NULL | |
| parent_email | varchar(255) | NO | | NULL | |
| parent_phone_number | varchar(255) | NO | | NULL | |
| first_name | varchar(255) | NO | | NULL | |
| last_name | varchar(255) | NO | | NULL | |
| days_absent | int(11) | YES | | NULL | |
| days_tardy | int(11) | YES | | NULL | |
| class_id | int(11) | NO | MUL | NULL | |
+----------------------+--------------+------+-----+---------+----------------+
mysql> describe attendance;
+-----------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------+------+-----+---------+-------+
| student_id | int(11) | NO | PRI | NULL | |
| class_id | int(11) | NO | PRI | NULL | |
| attendance_date | date | NO | PRI | NULL | |
| absent | tinyint(1) | YES | | NULL | |
| tardy | tinyint(1) | YES | | NULL | |
| note | text | YES | | NULL | |
+-----------------+------------+------+-----+---------+-------+
I want to check the attendance and email of all the children who are absent or tardy for the day. Is there a SQL statement I can use that can, for example, select the students from attendance with an absent/tardy value = 1, then using that student_id specified in the attendance table, pull all the student info from the student table where attendance.student_id = student.student_id?
just try exists
select student_email,student_phone_number from student std
where exists(
select 1 from attendance atd where std.student_id = atd.student_id
and (atd.absent =1 or atd.tardy =1)
)
just make sure you select all the info from student table and exists all the filter in attendance talbe. the condition is student_id.
Also if you need info from attendance talbe, then use join.

Not getting desired result using join in mysql

I tried left,right,inner join here is my tables
SHOP_OFFER_STATUS
+-----------------------+--------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------------------+----------------+
| MOBILE_NUM | varchar(100) | YES | | NULL | |
| SHOP_ID | varchar(100) | YES | | NULL | |
| OFFER_ID | int(255) | YES | | NULL | |
| OFFER_STATUS | varchar(100) | YES | | NULL | |
| OFFER_GENERATED_ID | int(255) | NO | PRI | NULL | auto_increment |
| OFFER_GENERATED_DATE | timestamp | NO | | CURRENT_TIMESTAMP | |
| OFFER_EXPIRY_DATE | timestamp | NO | | 0000-00-00 00:00:00 | |
| OFFER_CODE | varchar(10) | YES | | NULL | |
| AMOUNT | decimal(9,2) | YES | | NULL | |
| BILL_AMOUNT | decimal(9,2) | YES | | NULL | |
+-----------------------+--------------+------+-----+---------------------+----------------+
TRACK_TRANSACTIONS
+------------------+---------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+-------------------+----------------+
| TRANSACTION_ID | int(255) | NO | PRI | NULL | auto_increment |
| TRANCTION_TYPE | varchar(100) | YES | MUL | NULL | |
| TRANSACTION_DATE | timestamp | NO | | CURRENT_TIMESTAMP | |
| AMOUNT | decimal(10,2) | YES | | NULL | |
| MOBILE_NUM | varchar(15) | YES | | NULL | |
| SHOP_ID | varchar(200) | YES | | NULL | |
+------------------+---------------+------+-----+-------------------+----------------+
i know its my design problem and i have thousands of records(can't rollback or redesign),here how it works when ever person redeems the offer in transaction table shop_id and mobile_num will be added I am trying to get BILL_AMOUNT,TRANSACTION_DATE from this tables...but its giving wrong result...i want to know when(TRANSACTION_DATE ) and what amount he redeemed!
TRANSACTION table have many other transactions like usercreated,commented etc(TRANCTION_TYPE) and offer redemption are around TRANCTION_TYPE='MERCHANT CASHBACK' 100 records if i join this tables more then 50 thousands records comes in :( here is the query!
SELECT COUNT(*)
FROM TRACK_TRANSACTIONS T RIGHT JOIN
SHOP_OFFER_STATUS S
ON T.SHOP_ID=S.SHOP_ID
WHERE T.TRANCTION_TYPE='MERCHANT CASHBACK' AND S.OFFER_STATUS='REDEEMED';
the desired output:100 records where offer redemption happend!

Query Returns No Results Only When ORDER BY Added

I have a table with the following schema:
people_stages
id | person_id | stage_id | created
1 | 1 | 1 | 2013-09-01 00:00:00
2 | 1 | 2 | 2013-09-02 00:00:00
3 | 1 | 3 | 2013-09-03 00:00:00
I have created the following query to select the most recent stage grouped by person:
SELECT *
FROM people Person
LEFT JOIN people_stages PersonStage ON PersonStage.person_id = Person.id
WHERE PersonStage.created = (SELECT MAX(people_stages.created) FROM people_stages GROUP BY person_id HAVING person_id = PersonStage.person_id);
It works fine, however, if I try to ORDER BY a field in the Person table:
SELECT *
FROM people Person
LEFT JOIN people_stages PersonStage ON PersonStage.person_id = Person.id
WHERE PersonStage.created = (SELECT MAX(people_stages.created) FROM people_stages GROUP BY person_id HAVING person_id = PersonStage.person_id)
ORDER BY Person.last_name;
It returns 0 results.
Could anyone provide some insight, please?
Thanks!
EDIT: Structure of people
+----------------------------+--------------------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------------------------------------------------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| internal_id | varchar(50) | NO | MUL | NULL | |
| public_id | varchar(30) | NO | | NULL | |
| counselor_id | bigint(20) | NO | | NULL | |
| term_id | int(11) | NO | MUL | NULL | |
| program_id | int(11) | NO | | NULL | |
| person_type_id | int(11) | NO | MUL | NULL | |
| first_name | varchar(100) | NO | | NULL | |
| middle_name | varchar(100) | NO | | NULL | |
| last_name | varchar(100) | NO | | NULL | |
| photo_url | varchar(255) | NO | | NULL | |
| gender | enum('m','f','u') | NO | | NULL | |
| date_of_birth | date | NO | | NULL | |
| address | varchar(255) | NO | | NULL | |
| address_apt | varchar(100) | NO | | NULL | |
| address_city | varchar(100) | NO | | NULL | |
| address_state | varchar(100) | NO | | NULL | |
| address_state_intl | varchar(255) | NO | | NULL | |
| address_zip | varchar(25) | NO | | NULL | |
| address_country | varchar(100) | NO | | NULL | |
| address_verified | tinyint(1) | NO | | NULL | |
| address_latitude | varchar(100) | NO | | NULL | |
| address_longitude | varchar(100) | NO | | NULL | |
| address_position | point | NO | MUL | NULL | |
| address_distance | smallint(6) | NO | | NULL | |
| social_facebook | mediumtext | NO | | NULL | |
| social_twitter | varchar(255) | NO | | NULL | |
| social_instagram | varchar(255) | NO | | NULL | |
| phone_cell | varchar(25) | NO | | NULL | |
| phone_cell_clean | varchar(25) | YES | | NULL | |
| phone_work | varchar(25) | NO | | NULL | |
| phone_work_clean | varchar(25) | NO | | NULL | |
| permission_to_text | tinyint(1) | NO | | NULL | |
| permission_to_text_confirm | tinyint(1) | NO | | NULL | |
| phone_home | varchar(25) | NO | | NULL | |
| phone_home_clean | varchar(25) | YES | | NULL | |
| email_address | varchar(255) | NO | | NULL | |
| permission_to_email | tinyint(1) | NO | | NULL | |
| preferred_contact | enum('phone_home','phone_cell','text_cell','email','postal') | NO | | NULL | |
| parent_first_name | varchar(100) | NO | | NULL | |
| parent_last_name | varchar(100) | NO | | NULL | |
| parent_email | varchar(255) | NO | | NULL | |
| hs_name | varchar(255) | NO | | NULL | |
| hs_homeschooled | tinyint(1) | NO | | NULL | |
| hs_ceeb_id | varchar(100) | NO | | NULL | |
| hs_grad_year | varchar(4) | NO | | NULL | |
| coll_name | varchar(255) | NO | | NULL | |
| coll_ceeb_id | varchar(100) | NO | | NULL | |
| coll_major | varchar(255) | NO | | NULL | |
| coll_year | varchar(20) | NO | | NULL | |
| counselor_read | tinyint(1) | NO | | NULL | |
| source | varchar(100) | NO | | NULL | |
| entry_method | varchar(100) | NO | | NULL | |
| erp_processed | tinyint(1) | NO | | NULL | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
+----------------------------+--------------------------------------------------------------+------+-----+---------+----------------+
This appears to be a bug in MySQL, about which I have filed a report. I have narrowed it to the following test case, which one would expect to return a single record (but it does not):
CREATE TABLE t (x INT NULL); -- table with nullable column
INSERT INTO t VALUES (0); -- but non null data
SELECT a.x -- select our nullable column
FROM t a, (SELECT NULL) b -- joining it with anything at all
WHERE EXISTS ( -- but filter on a subquery
SELECT *
FROM (SELECT NULL) c -- doesn't really matter what
HAVING a.x IS NOT NULL -- provided there is some correlated condition
-- on our nullable column in the HAVING clause
)
ORDER BY RAND() -- then perform a filesort on the outer query
See it on sqlfiddle.
In your case, you can do a number of things to fix this:
Avoid the correlated subquery by rewriting as a join:
SELECT *
FROM people AS p LEFT JOIN (people_stages AS s NATURAL JOIN (
SELECT person_id, MAX(created) created
FROM people_stages
GROUP BY person_id
) t) ON s.person_id = p.id
ORDER BY p.last_name
If you want to keep the correlated subquery (which can generally yield poor performance but is often easier to understand), use WHERE instead of HAVING:
SELECT *
FROM people AS p LEFT JOIN people_stages AS s ON s.person_id = p.id
WHERE s.created = (
SELECT MAX(created)
FROM people_stages
WHERE person_id = s.person_id
)
ORDER BY p.last_name
If you're unable to change the query, you should find that making the people_stages.person_id column non-nullable will get around the problem:
ALTER TABLE people_stages MODIFY person_id BIGINT UNSIGNED NOT NULL
It seems that having an index on that column (which would be required to effect a foreign key constraint) may also help:
ALTER TABLE people_stages ADD FOREIGN KEY (person_id) REFERENCES people (id)
Alternatively one could remove people_stages.person_id from the select list, or adjust the data model/indexing/query strategy to avoid a filesort (may not be practical in this case, but I mention them here for completeness).
Check that you are not running out of space in your server... yes, sounds strange, but a behavior like the one described could be caused by that