How can COUNT be used multiple times when creating a view table? - mysql

So, I am currently trying to create a View Table with the following info: EventCode, event name, the number of shows, the screen cost, the total screening cost (number of shows * screening cost), promotion cost, production cost, total cost (total screen cost + promotion cost + production cost), the expected
revenue (use the BaseTicketPrice), the actual revenue, the total discounts given (expected revenue - actual revenue) and the profit (all costs - actual revenue).
So there is a lot of information math involved in this View.
Also, the information comes from multiple tables, about 4.
I have to find the number of shows which would come from the table EventShow and then I have to use the number of shows in a later equation. How do I structure the count of a table and then use it again in an equation/aggregation?
I realize what I have below is really incorrect, but I'm mainly struggling on the COUNT portion right now.
CREATE OR REPLACE VIEW TicketDetails AS
SELECT
E.EventCode,
E.EventName,
E.EventShows Count(*) as NumberOfShows,
(E.ScreeningCost * NumberOfShows) as TotalScreeningCost,
(E.BaseTicketPrice * Ticket(Count(*))) as ExpectedRevenue,
E.PromotionCost,
E.ProductionCost,
(TotalScreeningCost + E.PromotionCost + E.ProductionCost) as TotalCost,
(E.BaseTicketPrice * TS.TicketsSolds) as ActualRevenue,
(ExpectedRevenue - ActualRevenue) as TotalDiscounts
(TotalCost - ActualRevenue) as Profit
FROM Event E, Ticket T, TicketSale TS, EventShow ES
WHERE (E.EventCode = TS.EventCode);

The select list is one the last clauses that get executed in sql. Any aliases defined in the select list can only be referred to in the order by and having clauses, but not in the select list or where clause.
Another issue will be that if you join multiple tables, you cannot count the rows returned from one of the tables using count(*) or count(tablename.*), so you have to use a field from that table which cannot be null if a row is returned from that table - this should be a field you joined the table on. A simplified example based on your tables:
select e.eventcode, e.eventname, count(es.eventcode) as numberofshows, e.screeningcosts*count(es.eventcode) as totalscreeningcosts
from events e left join eventshows es
on e.eventcode=es.eventcode
group by e.eventcode, e.eventname, e.screeningcosts
I assumed that the 2 tables each have an eventcode field.

Related

entries in multiple dates in access

I have a access table that has quarterly pricing data starting from 20100131 and goes on as 20100430, 20100731.... 20170131, 20170430. For each pricing date, there are many loans. Some loans stay in the portfolio, some loans are removed and some added for each pricing period. I would like to find the list of loans that exist in all periods and see their price for each period. So i have the "Loan_Number" field and "Price_Date" field. I would like to find the Loan Numbers that exist in all price date points. I appreciate the help.
Thanks!
Would have been nice to see some effort from you but I was intrigued with the challenge so here is what I accomplished.
1 - Need a dataset of all possible combinations of loan numbers and date values. So if you have a LoanNumbers table and a Periods table, create a Cartesian query called AllPairs:
SELECT LoanNumbers.Loan_Number, Periods.Price_Date FROM LoanNumbers, Periods;
If you don't have those tables, generate datasets with queries, assuming the data table has at least one record for every loan number and at least one record for every period:
SELECT DISTINCT Table1.Price_Date FROM Table1;
SELECT DISTINCT Table1.Loan_Number FROM Table1;
2 - Join AllPairs to data table for a 'find unmatched' query called LoanNoPeriod:
SELECT AllPairs.Loan_Number, AllPairs.Price_Date, Table1.Loan_Number, Table1.Price_Date
FROM AllPairs LEFT JOIN Table1 ON (AllPairs.Price_Date = Table1.Price_Date) AND (AllPairs.Loan_Number = Table1.Loan_Number)
WHERE (((Table1.Price_Date) Is Null));
3 - Final query:
SELECT * FROM Table1 WHERE Loan_Number NOT IN (SELECT AllPairs.Loan_Number FROM LoanNoPeriod);
Be aware these type of queries can perform very slowly and with very large datasets might not be practical.

Pulling different records from multiple tables as one transaction history list

I am working on an employee management/reward system and need to be able to show a single "transaction history" page that shows in chronological order the different events that the employee has experienced in one list. (Sort of like how in facebook you can goto your history/action section and see a chronological list of all the stuff that you have done and affects you, even though they are unrelated to eachother and just have you as a common user)
I have different tables for the different events, each table has an employee_id key and an "occured" timestamp, some table examples:
bonuses
customers
raise
complaints
feedback
So whenever an event occurs (ie a new customer is assigned to the employee, or the employee gets a complaint or raise) a new row is added to the appropriate table with the employee ID it affects and a timestamp of when it occured.
I need a single query to pull all records (upto 50 for example) that include the employee and return a history view of that employee. The field names are different in each table (ie the bonus includes an amount with a note, the customer includes customer info etc).
I need the output to be a summary view using column names such as:
event_type = (new customer, bonus, feedback etc)
date
title (a brief worded title of the type of event, specified in sql based on the table its referencing)
description (verbiage about the action, such as if its event_type bonus display the bonus amount here, if its a complain show the first 50 characters of the complaint message or the ID of the user that filed the complaint from the complaints table. All done in SQL using if statements and building the value of this field output based on which table it comes from. Such as if its from the customers table IF current_table=customers description='A customer was assigned to you by'.customers.assigner_id).
Ideally,
Is there any way to do this?
Another option I have considered, is I could do 5-6 different queries pulling the records each from their own table, then use a mysql command to "mesh/interleave" the results from all the queries into one list by chronological order. That would be acceptable too
You could use a UNION query to merge all the information together and use the ORDER BY clause to order the actions chronologically. Each query must have the same number of fields. Your ORDER BY clause should be last.
The examples below assume you have a field called customer_name in the customers table and bonus_amount in the bonuses table.
It would look something like this:
SELECT 'New Customer' as event_type, date,
'New customer was assigned' as title,
CONCAT('New Customer: ', customer_name, ' was assigned') as description
FROM customers
WHERE employee_id = 1
UNION
SELECT 'Bonus' as event_type, date,
'Received a bonue' as title,
CONCAT('Received a bonus of $', FORMAT(bonus_amount, 2), '.') as description
FROM bonuses
WHERE employee_id = 1
UNION
...
ORDER BY date DESC;

Finding the sum of a field in a linked table per group with additional search criteria

My actual tables are much more complex but here is a simplified example of the problem I am trying to work out.
Table contact: ContactID, ContactName, Pending
Table purchase: PurchaseID, ContactID, Amount, Pending, Date
Table contact_purchase_link: ContactID, PurchaseID (although it may seem like the link table is not necessary in this simplified example it is necessary in the large table schema)
Here is the query that I currently have:
SELECT DISTINCT contact.ContactID,
( SELECT SUM(Amount)
FROM purchase
WHERE purchase.ContactID = contact.ContactID
AND purchase.Pending = 0
) totalpurchase
FROM contact
INNER JOIN ( contact_purchase_link JOIN purchase
ON (contact_purchase_link.PurchaseID = purchase.PurchaseID
))
USING (ContactID)
WHERE purchase.Date > '2013-12-06' AND
AND contact.Pending =0
The problem is that I want the totalpurchase (the sum of the amount field) to be limited to the search criteria of the purchase table - meaning the query should only return the sum of the purchases after the specified date per contact. I think in order to use a group by clause the query would have to be based off the purchase table but I need the query to use the contact table so that all contacts are listed with their total purchase amounts and other relevant client data.
Is there any way to do this within one query?
To further clarify:
This query is being generated as part of a search engine. An example of why a query like this would be done is if a user wanted to generate a contact list of lastnames starting with A with purchases of a specific item or as in this example of purchases for a specific date. So that in general the query would have to generate a list of all contacts and their data (with possible search criteria on the type of contact such as all lastnames starting with 'A' etc.) and the query can also include search criteria on the purchase table such as the date of the purchase and whether the purchase was for specific items etc.
I am trying to add in the option to also list the sum of the purchases for the contact however that sum has to be limited to the search criteria for the purchase table as well and not the sum of all the contacts purchases.
If I understand your question correctly, you need to move the date comparison inside the first subquery:
SELECT DISTINCT contact.ContactID,
( SELECT SUM(Amount)
FROM purchase
WHERE purchase.ContactID = contact.ContactID
AND purchase.Pending = 0
AND purchase.Date > '2013-12-06'
) totalpurchase
FROM contact
INNER JOIN ( contact_purchase_link JOIN purchase
ON (contact_purchase_link.PurchaseID = purchase.PurchaseID
)
USING (ContactID)
WHERE purchase.Date > '2013-12-06'
AND contact.Pending =0
But the comments are right - I corrected a couple of what appears to be syntax errors, and I'm not sure about the join to contact_purchase_link. Improve your question and my answer will be less like guesswork.

Adding a query (new join) to an existing join

So I have three databases (all on one server) that I need to join tables on. Essentially I do a join across two tables to determine the identification of particular users who do a certain thing after a certain date. It works fine:
SELECT a.THING, a.ANOTHERTHING, b.IDENTIFICATION, b.RELEVANTDATE
FROM FIRSTDATABASE.TABLE a
JOIN SECONDDATABASE.TABLE b
ON a.THING = b.THING
WHERE ANOTHERTHING = '----' AND IDENTIFICATION <> 'NULL'
AND b.RELEVANTDATE > date('YYYY-MM-DD')
At present I'm also running a second query by its lonesome - this is one table, on a third database - to get all users with a certain amount of an item. It also works:
SELECT ITEM, AMOUNT, IDENTIFICATION
FROM TABLE
WHERE ITEM = '----' AND AMOUNT > '0' AND IDENTIFICATION <> 'NULL'
GROUP BY AMOUNT
I then, using the first table as my guide, use VLOOKUP so I can get the AMOUNT generated in the second query for each and every user IDENTIFICATION meeting the criteria after a certain date AND who did a certain thing, from the first query.
My question is, how would I join these two into one large query?

How do I model product ratings in the database?

What is the best approach to storing product ratings in a database? I have in mind the following two (simplified, and assuming a MySQL db) scenarios:
Create two columns in the products table to store the number and the sum of all votes respectively. Use the columns to get an average at run time or using a query.
This approach means I only need to access one table, simplifying things.
Normalize the data by creating an additional table to store the ratings.
This isolates the ratings data into a separate table, leaving the products table to furnish data on available products. Although it would require a join or a separate query for ratings.
Which approach is best, normalised or denormalised?
A different table for ratings is highly recommended to keep things dynamic. Don't worry about hundreds (or thousands or tens of thousands) of entries, that's all peanuts for databases.
Suggestion:
table products
id
name
etc
table products_ratings
id
productId
rating
date (if needed)
ip (if needed, e.g. to prevent double rating)
etc
Retrieve all ratings for product 1234:
SELECT pr.rating
FROM products_ratings pr
INNER JOIN products p
ON pr.productId = p.id
AND p.id = 1234
Average rating for product 1234:
SELECT AVG(pr.rating) AS rating_average -- or ROUND(AVG(pr.rating))
FROM products_ratings pr
INNER JOIN products p
ON pr.productId = p.id
AND p.id = 1234;
And it's just as easy to get a list of products along with their average rating:
SELECT
p.id, p.name, p.etc,
AVG(pr.rating) AS rating_average
FROM products p
INNER JOIN products_ratings pr
ON pr.productId = p.id
WHERE p.id > 10 AND p.id < 20 -- or whatever
GROUP BY p.id, p.name, p.etc;
I know that my answer is not what you actually ask for, but you might want to have a chance of facilitating that new products with your system can almost never beat the old products. Say that you would get a product with 99% rating. It would be very difficult for new products to get high if you sort by products with the highest rating.
Do not store a record of each rating unless you absolutely need them specifically. An example of such a case could be a psychological experiment that tends to analyze specific properties of the raters themselves. So, yeah! You'd have to be just as crazy to store each rate in a separate record.
Now, coming to the solution, add two more columns to your product table: AverageRating and RateCount.
What would you store in them? Well, suppose you have an already-calculated average of the two numbers: 2 and 3, which is 2.5; having a new rate of 10, you'll multiply the average (2.5) by the rate count (2 in this case). Now, you have 5. Add this result to the new rate value (10) and divide the result by 3.
Let's cover all the above in a simple formula,
(AverageRating * RateCount + NewRateValue) / (RateCount + 1)
So (2.5 * 2 + 10) / (2 + 1) = 5.
Calculate the average on the server-side (not in your database) and store the average in the AverageRating column and the rate count in the RateCount column.
Simple, right?!
Edit
This solution doesn't require storing each rating separately as long as no review, edit or delete operations are involved. Yet, for such cases; let's assume that you've got a review with a rating of 3 that the owning user would like to modify to 4. Then, the formula to recalculate the average rating would be like this,
(AverageRating * RateCount - OldRateValue + NewRateValue) / RateCount
References
https://math.stackexchange.com/a/106314