I have 2 tables, customer and tickets
I want to be able to select from the tickets table and order by:
tickets.priority then customer.category_priority then tickets.queue_time
I have this query:
SELECT t.*
from tickets t
JOIN customer c ON t.company = c.sequence
WHERE t.status <> 'Completed' AND t.queue = 'Y'
ORDER BY field(t.priority, 'Critical', 'High', 'Medium', 'Low'), t.queue_time ASC
which works great for the tickets.priority and tickets.queue_time
but im not sure how to incorporate the customer.category_priority
so in the customer table, i have columns with names like:
priority_computers
priority_telephone
priority_software
all INT fields and have a value of 0, 1 or 2
the row in tickets has a category column which is either Computers, Telephone, or Software and thats what needs to link to the above.
so, if a customer row has priority_computers of 2 and the tickets row is category = 'Computers' that would be at the top of the list because the customer record has the priority of 2 and it would also incorporate the other ORDER BY conditions
Examples:
Customers:
Company A priority_computers = 1
Company B priority_computers = 2
Company C priority_computers = 3
Example One:
Ticket 1 Company A priority = Medium category = Computers
queue_time = 2015-11-20 08:00
Ticket 2 Company B priority = Medium category = Computers
queue_time = 2015-11-20 10:00:00
Ticket 3 Company C priority = Medium category = Computers
queue_time = 2015-11-20 08:30:00
This should output in the following order:
Ticket 3
Ticket 2
Ticket 1
Example 2:
Ticket 1 Company B priority = High category = Computers
queue_time = 2015-11-20 12:00
Ticket 2 Company A priority = Medium category = Computers
queue_time = 2015-11-20 07:00:00
Ticket 3 Company C priority = Medium category = Computers
queue_time = 2015-11-20 07:00:00
This should output in the following order:
Ticket 1
Ticket 3
Ticket 2
If I understand this correctly, then your problem is that you have to match data with column names somehow.
customer.priority_computers for ticket.category = 'Computers'
customer.priority_telephone for ticket.category = 'Telephone'
customer.priority_software for ticket.category = 'Software'
This shows a database design flaw. There should be a customer_priority table instead with each row holding a customer, a category and the associated value. Then you could simply join.
As is, you must check data content and decide for a column to use in your query:
SELECT t.*
from tickets t
JOIN customer c ON t.company = c.sequence
WHERE t.status <> 'Completed' AND t.queue = 'Y'
ORDER BY field(t.priority, 'Critical', 'High', 'Medium', 'Low')
, case t.category
when 'Computers' then c.priority_computers
when 'Telephone' then c.priority_telephone
when 'Software' then c.priority_software
end
, t.queue_time ASC
Update: Here is a query you'd write, if you had a customer_priority table. You see, your query doesn't need to know what categories exist and how to treat them any longer.
SELECT t.*
from tickets t
JOIN customer c ON t.company = c.sequence
JOIN customer_priority cp ON cp.customer_id = c.sequence
AND cp.category = t.category
WHERE t.status <> 'Completed' AND t.queue = 'Y'
ORDER BY field(t.priority, 'Critical', 'High', 'Medium', 'Low')
, cp.priority_value
, t.queue_time ASC
Moreover: As mentioned, it is strange to have a table customer, but in tickets it's not called a customer, but a company, and in the customer table itself it's not called a customer number or ID either, but a sequence. This makes the queries less readable. I suggest, you change the names, if possible, so they are consistent.
Also your query shouldn't have to know what 'Critical' and 'High' means. There should be a table for priorities where each priority has a name and a value, so the query could simply pick the value and work with it without having to know anything else about the priorities.
Related
I have 3 MYSQL Tables User table and 2 Transactions table.
My User table is as follows.
id inviter_id active .... (other columns)
2 1 1
3 1 1
4 2 1
5 1 1
6 2 1
My txn1 table is
id payer_id receiver_id amount type
1 2 1 20 profit
2 3 1 30 profit
3 4 2 20 profit
4 3 2 50 profit
5 5 2 20 profit
My txn2 table is
id payer_id receiver_id amount txn_type
1 2 1 20 profit
2 3 2 30 profit
3 4 2 20 profit
4 3 1 50 profit
5 5 1 20 profit
What I need to get is,
Consider I'm querying for the user 2. I need the profit he has earned from each of his downline or referrals using the inviter_id column.
For example, If I want to get the profit of User #2 from Txn1 and Txn2 table, it should get the txns made by user 4 and user 6 in txn1 and txn2 tables.
What I have tried so far is,
$userID = 2;
$this->db->select('u.id as partner_id, SUM(txn1_profit.amount) AS t1Profit, SUM(txn2_profit.amount) AS t2Profit');
$this->db->from('users u');
$this->db->join('txn1 txn1_profit', "u.id = txn1_profit.payer_id AND $userID = txn1_profit.receiver_id AND 'profit' = txn1_profit.txn_type",'LEFT');
$this->db->join('txn2 txn2_profit', "u.id = txn2_profit.payer_id AND $userID = txn2_profit.receiver_id AND 'profit' = txn2_profit.txn_type",'LEFT');
$this->db->where('u.inviter_id', $userID);
$this->db->group_by('u.id');
$query = $this->db->get();
$row = $query->result();
if (empty($row))
return FALSE;
return $row;
The problem with this query is i'm getting a huge sum value.
If schema of your transaction tables is same then I would suggest you to change your schema and have one single transaction table to store such information. By changing your current design will help you to build simple queries may reduce no. of joins.
For your current schema I can think of 2 possible ways to address your profit value issue.
Calculate sum from your transaction tables in sub-clause and then do a join with users table
select u.inviter_id,
sum(t1.t1Profit),
sum(t2.t2Profit),
sum(t1.t1Profit) + sum(t2.t2Profit) total
from users u
left join (
select payer_id, sum(amount) t1Profit
from tx1
where 2 = receiver_id
and 'profit' = type
group by payer_id
) t1 on u.id = t1.payer_id
left join (
select payer_id, sum(amount) t2Profit
from tx2
where 2 = receiver_id
and 'profit' = txn_type
group by payer_id
) t2 on u.id = t2.payer_id
where u.inviter_id = 2;
Or combine data of your transaction tables using union all and then do a join with users table
select u.inviter_id,
sum(t1.amount) total
from users u
left join (
select payer_id, amount
from tx1
where 2 = receiver_id
and 'profit' = type
union all
select payer_id, amount
from tx2
where 2 = receiver_id
and 'profit' = txn_type
) t1 on u.id = t1.payer_id
where u.inviter_id = 2;
DEMO
► Context : I work in a museum (for real), people come everyday, they buy tickets for themselves (humans) and sometimes they also buy tickets for drinks and foods (objects). There are events, tickets have the same names per event but the prices are different.
► The problem : I have to create a report with 2 results : total sales (visitors + food + drinks) and how many people came (visitors only) for a specific event. Next is an image of the 3 tables in the database, how they relate and some sample data :
Table TICKETS relates to SALES_MAIN through EVENT_ID column.
Table SALES_MAIN relates to SALES_DETAIL through ID→MAIN_ID columns.
Table SALES_DETAIL have a column TICKET_NAME but it's not unique in table TICKETS.
► The question : How to get both results, total sales and human count, for event 555 in one "select" ? I tried next 2 "select" but when I combine them with another INNER JOIN I get cartesian results :
Get detail sales for event 555 :
SELECT sales_detail.* FROM sales_main
INNER JOIN sales_detail ON sales_detail.main_id = sales_main.id
WHERE sales_main.event_id = '555'
Get tickets for event 555 :
SELECT * FROM tickets WHERE tickets.event_id = '555'
Use:
SELECT
SUM(CASE WHEN sd.ticket_name IN ('adult', 'child') THEN sd.quantity
ELSE 0 END) AS total_visitors,
SUM(sd.quantity * t.price) AS total_sales
FROM sales_main sm
JOIN sales_detail sd
ON sd.main_id = sm.id
JOIN ticket t
ON t.event_id = sm.event_id
AND t.ticket_name = sd.ticket_name
WHERE sm.event_id = '555';
Conditional aggregation could also be based on type:
SUM(CASE WHEN t.ticket_type ='human' THEN sd.quantity ELSE 0 END)
Can someone help me how to make an efficient mysql query for this?
I need to make a query to get the sum quantity of all ordered items.
combined with.
I need to make a query to get the sum quantity of all received items.
Please note that some products have serial numbers when received and some don't in my 'received_po_details' table. That's where I'm having problem with since the sum of received gets doubled because of the serial numbers.
Table: received_po_details
i_rpoh_id i_p_id i_quantity_received s_product_serial
1 1 100
1 2 100
1 3 50
1 4 25
1 7 100
1 8 50
1 6 1 XYZ1
1 6 1 XYZ2
1 5 1 ABC1
1 5 1 ABC2
Right now I have these 2 separate sql that I need to combine.
I don't want to use Union statement for this if possible...
-- to get the Total Quantity Ordered
SELECT
products.i_id AS 'ID',
products.s_name AS 'Name',
COALESCE(SUM(purchase_order_details.i_quantity_ordered),0) AS 'Total Quantity Ordered'
FROM
products
LEFT JOIN
purchase_order_details
ON
products.i_id = purchase_order_details.i_p_id
GROUP BY
products.i_id
-- to get the Total Quantity Received
SELECT
products.i_id AS 'ID',
products.s_name AS 'Name',
COALESCE(SUM(received_po_details.i_quantity_received),0) AS 'Total Quantity Received'
FROM
products
LEFT JOIN
received_po_details
ON
products.i_id = received_po_details.i_p_id
GROUP BY
products.i_id
You can combine these queries by creating a derived table or each type of sum (in the query below they're named t1 and t2) and left joining the derived tables to the main product table.
SELECT
p.i_id AS 'ID',
p.s_name AS 'Name',
COALESCE(t1.total,0) 'Total Quantity Ordered',
COALESCE(t2.total,0) 'Total Quantity Received'
FROM products p
LEFT JOIN (
SELECT
pod.i_p_id,
SUM(pod.i_quantity_ordered) total
FROM purchase_order_details pod
GROUP BY pod.i_p_id
) t1 ON t1.i_p_id = p.i_id
LEFT JOIN (
SELECT
rpd.i_p_id,
SUM(rpd.i_quantity_received) total
FROM received_po_details rpd
GROUP BY rpd.i_p_id
) t2 ON p.i_id = t2.i_p_id
I have the following tables, in a standard shop:
(id is always primary key, auto-increment, ts is always type TIMESTAMP, updated ON_UPDATE CURRENT_TIMESTAMP)
table sales:
id | total | tendered | flag | userID | ts
1 0.6 0.6 0 4 2013-11-21 08:12:23
Sales is the parent table, userID is related to the user that made the sale. total and tendered are both of type FLOAT. flag is of type VARCHAR and could be Free Order.
table receipts:
id | oID | pID | quantity | ts
1 1 26 1 2013-11-21 08:11:25
Receipts holds a line for each unique type of product sold. oID is type INT and relates to the id of table sales. pID is of type INT and relates to the id of table products.
table products:
id | name | price | cID | display | ts
1 Mars 0.6 3 1 2014-01-17 07:55:25
Products is the central data for each product in the database. Here is a line for mars bars. cID relates to the id in table categories.
table categories
id | name | display | ts
3 Snacks 1 2013-11-14 12:06:44
Categories is the table holding all the data about each category, and can have multiple products relating to a single row. display is of type INT and dictates when the category is enabled or disabled (1 = 'true')
My question is, I want to output information like this:
**Snacks**
(name) (quantity) (price) (total)
Fruit 3 50p £1.50
Twix 1 60p 60p
Boost 1 60 60p
**Hot Drinks**
(name) (quantity) (price) (total)
English Tea 15 60p £9.00
Speciality Teas 2 60p £1.20
Which I have the following SQL for:
SELECT categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts WHERE pID=r.pID AND DATE(ts) = CURDATE()) AS quantity,
products.price,r.ts
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN categories ON products.cID = categories.id
WHERE DATE(r.ts) = CURDATE()
GROUP BY r.pID
ORDER BY categories.name;
Which seems to give me the correct information, but I am not 100% certain. If anyone could verify that this works, I would be most grateful. But when I want to see a particular day, I get unusual figures with the following SQL:
$postfrom = $_POST['from_mm']."/".$_POST['from_dd']."/20".$_POST['from_yy'];
$postto = $_POST['to_mm']."/".$_POST['to_dd']."/20".$_POST['to_yy'];
$from = strtotime($postfrom . " 6:00");
$to = strtotime($postto . " 23:59");
$itemised = select("SELECT categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts WHERE pID = r.pID AND UNIX_TIMESTAMP(r.ts) > '{$from}' AND UNIX_TIMESTAMP(r.ts) < '{$to}')
AS quantity, products.price
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN categories ON products.cID = categories.id
WHERE UNIX_TIMESTAMP(r.ts) > '{$from}'
AND UNIX_TIMESTAMP(r.ts) < '{$to}'
GROUP BY r.pID
ORDER BY categories.name;");
(function 'select' simply returns an array of the SQL table). The thing is, I could find the results easily by looping through in PHP and adding it up that way. But I know this is possible with SQL, I just don't know why It isnt working. Can somebody please help?
Edit SQL sample fiddle is here: http://sqlfiddle.com/#!2/23af4 although I couldn't do more than half a day of data due to 8000 character restrictions.
Try this:
SELECT categories.name AS category, products.name AS name,
receipts.quantity AS quantity, products.price AS price,
(receipts.quantity * products.price) AS total
FROM categories
JOIN products
ON categories.id = products.cID
JOIN receipts
ON receipts.pID = products.ID
WHERE DATE(receipts.ts) = CURDATE()
ORDER BY categories.name
SQLFiddle demo
With regard to the date restriction, you could use BETWEEN ... AND ... to specify the date and time. Using an absolute date and time moment or relative to the current day and time, for example WHERE DATE(receipts.ts) BETWEEN concat(curdate() -5,' 6:00:00 AM') AND curdate() -4
I am running this query
SELECT sh.*,
u.initials AS initals
FROM database1.table1 AS sh
JOIN database2.user AS u ON u.userID = sh.userid
WHERE id = 123456
AND dts = ( SELECT MAX(dts) from database1.table1 )
ORDER BY sort_by, category
In the table1 I have records like this
dts status category sort_by
2010-04-29 12:20:27 Civil Engineers Occupation 1
2010-04-28 12:20:27 Civil Engineers Occupation 1
2010-04-28 12:20:54 Married Marital Status 2 2010-04-28 12:21:15 Smoker Tobbaco 3
2010-04-27 12:20:27 Civil Engineers Occupation 1
2010-04-27 12:20:54 Married Marital Status 2 2010-04-27 12:21:15 Smoker Tobbaco 3
2010-04-26 12:20:27 Civil Engineers Occupation 1
2010-04-26 12:20:54 Married Marital Status 2 2010-04-26 12:21:15 Smoker Tobbaco 3
If you look at my data, I am choosing the latest entry by category and sort_id. however in some case such as on 29th (2010-04-29 12:20:27) I have only one record. So in this case I want to show occupation for latest and then the rest of them (latest). But currently it displays only one row.
trying as best to guess at what you have, your query would have to be adjusted anyhow, something like
SELECT
sh.dts,
cast( if( preAgg1.totalPerCatSort = 1, sh.status, ' ' ) as char(20))
as SingleStatus,
sh.category,
sh.sort_by,
u.initials AS initals
FROM
database1.table1 AS sh,
database2.user AS u,
( select
t1.category,
t1.sort_by,
max(dts) maxdts,
count(*) totalPerCatSort
from
database1 t1
group by
t1.category,
t1.sort_by ) preAgg1
WHERE
sh.userid = u.userID
and sh.sort_by = preAgg1.sort_by
and sh.category = preAgg1.category
and sh.dts = preAgg1.maxdts
and id = 123456 ?? Should this actually be the UserID column ??
ORDER BY
sort_by,
category
you may have to apply the "ID = 123456" into the "preAgg1" sub-select, but again, not sure which ID that column / table was applicable for, otherwise, you could have other date entries by other "users" and not the same for the one candidate person you are looking for... Your call.
Per your concern of getting ALL records, I would just remove your "and ID = 123456" qualifier and all should be good