SQL Subtracting and comparison - mysql

I am currently a student creating a system for our capstone project. I want to ask how can I display the difference of 2 result in sql. I am using phpmyadmin.
This was my query
SELECT *, sum(no_sec * hpw) AS total_teaching_hours from facultyload GROUP BY sy ORDER BY sy DESC
and displays like this
I was hoping to get something like this
that shows the differences of the data how much was increased or decreased
Thank you for the helppppppp

Related

Passing value to LIMIT function using SELECT query

There is a table named STATION.
I want to display half rows of the table.
MYSQL query
SELECT *
FROM STATION
LIMIT (SELECT COUNT(ID) FROM STATION)/2
I tried to perform a query like this but I am getting syntax error.
What is wrong in this query?
How can I perform this query?
One method is to use window functions:
select t.*
from (select t.*,
ntile(2) over (order by id) as tile
from t
) t
where tile = 1;
I have never seen a need of querying exactly half the table.
If you are asking this out of curiosity, that's fair but if there is really a need where you are trying to implement something like this, please revisit the design.
Coming to your question, you can possibly do two things:
Implement a stored procedure and query the count and store in a variable and then pass it on to the actual SELECT query for the LIMIT clause.
From your client code fire 2 different queries - 1 for count and calculate half (non fraction) and then pass it to the limit clause of next select query .

Do we have a workaround to use alias with 'where' in sql

Sales :
Q1) Return the name of the agent who had the highest increase in sales compared to the previous year
A) Initially I wrote the following query
Select name, (sales_2018-sales_2017) as increase
from sales
where increase= (select max(sales_2018-sales_2017)
from sales)
I got an error saying I cannot use increase with the keyword where because "increase" is not a column but an alias
So I changed the query to the following :
Select name, (sales_2018-sales_2017) as increase
from sales
where (sales_2018-sales_2017)= (select max(sales_2018-sales_2017)
from sales)
This query did work, but I feel there should be a better to write this queryi.e instead of writing where (sales_2018-sales_2017)= (select max(sales_2018-sales_2017) from sales). So I was wondering if there is a work around to using alias with where.
Q2) suppose the table is as following, and we are asked to return the EmpId, name who got rating A for consecutive 3 years :
I wrote the following query its working :
select id,name
from ratings
where rating_2017='A' and rating_2018='A' and rating_2019='A'
Chaining 3 columns (ratings_2017,rating_2018,rating_2019) with AND is easy, I want know if there is a better way to chain columns with AND when say we want to find a employee who has rating 'A' fro 10 consective years.
Q3) Last but not the least, I'm really interested in learning to write intermediate-complex SQL queries and take my sql skills to next level. Is there a website out there that can help me in this regard ?
1) You are referencing an expression with a table column value, and therefore you would need to define the expression first(either using an inline view/cte for increase). After that you can refer it in the query
Eg:
select *
from ( select name, (sales_2018-sales_2017) as increase
from sales
)x
where x.increase= (select max(sales_2018-sales_2017)
from sales)
Another option would be to use analytical functions for getting your desired results, if you are in mysql 8.0
select *
from ( select name
,(sales_2018-sales_2017) as increase
,max(sales_2018-sales_2017) over(partition by (select null)) as max_increase
from sales
)x
where x.increase=x.max_increase
Q2) There are alternative ways to write this. But the basic issue is with the table design where you are storing each rating year as a new column. Had it been a row it would have been more easy.
Here is another way
select id,name
from ratings
where length(concat(rating_2017,rating_2018,rating_2019))-
length(replace(concat(rating_2017,rating_2018,rating_2019)),'A','')=3
Q3) Check out some example of problems from hackerrank or https://msbiskills.com/tsql-puzzles-asked-in-interview-over-the-years/. You can also search for the questions and answers from stackoverflow to get solutions to tough problems people faced
Q1 : you can simply order and limit the query results (hence no subquery is necessary) ; also, column aliases are allowed in the ORDER BY clause
SELECT
name,
sales_2018-sales_2017 as increase
FROM sales
ORDER BY increase DESC
LIMIT 1
Q2 : your query is fine ; other options exists, but they will not make it faster or easier to maintain.
Finally, please note that your best option overall would be to modify your database layout : you want to have yearly data in rows, not in columns ; there should be only one column to store the year instead of several. That would make your queries simpler to write and to maintain (and you wouldn’t need to create a new column every new year...)

SQL Replace Join With Something Faster [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have a view in SQL that uses a join and takes a lot longer than I would like for it to take. I think it would run much faster if I converted it to a subquery instead, but I'm having trouble with that.
Basically, I want to create a "target" column that calculates the 24h percent change of the price of an asset. Right now, the way I go about it is I create a first view which is the normal table and then a second view which is a copy of the first table but with date+1 that I can then use to calculate the 24h target. Below is my sql code. I am working in MySQL.
create view PricesView1 as
select Date,Symbol, avg(Price) as 'Price', avg(BTC_Dominance) as 'BTC_Dominance',
pkdummy,pkey from Prices group by Date,pkdummy,pkey, Symbol
having right(pkdummy,2)=22 and Date > '2018-11-22';
create view PricesView2 as
select sq.Date, sq.oldDate, sq.Symbol, sq.Price, newP.Price as 'NewPrice',
newP.BTC_Dominance as 'NewBTCdominance', newP.pkdummy from (
select date_add(Date, INTERVAL 1 DAY) as 'Date', Date as 'oldDate',Symbol,avg(Price) as 'Price',
avg(BTC_Dominance) as 'BTC_Dominance', pkdummy,pkey from Prices
group by Date,date_add(Date, INTERVAL 1 DAY),pkdummy,pkey, Symbol having right(pkdummy,2)=22)sq
join Prices newP on newP.Date=sq.Date and newP.Symbol=sq.Symbol
where right(newP.pkdummy,2)=22 and sq.Date > '2018-11-22' order by datetime desc;
#Use other two views to calculate target
create view priceTarget as
select pv1.Date, pv1.Symbol, avg(pv1.Price) as 'Initial Price', avg(pv2.NewPrice) as 'Price24hLater',
avg(((pv2.NewPrice-pv1.Price)/pv1.Price)*100) as 'Target24hChange',
avg(((pv2.NewBTCdominance-pv1.BTC_Dominance)/pv1.BTC_Dominance)*100) as 'BTCdominance24hChange',
pv1.pkey from PricesView1 pv1
join PricesView2 pv2 on pv1.Date=pv2.oldDate and pv1.Symbol=pv2.Symbol
group by pv1.Date, pv1.Symbol;
Here is a screenshot of the output of the query: SELECT * FROM priceTarget WHERE symbol = 'btc' ORDER BY date desc;
Any thoughts on how I can achieve the same result with a faster query that avoids using a join?
Any help would be very much appreciated!
EDIT: I guess it just comes down to the fact that I simply have a lot of data being loaded. I created a new first view to filter my data ahead of time and that reduced the load times from around 32 seconds to just over 10 seconds. Thanks to those who helped!
In the creation of PriceView2 there seems to be some unecessary code
Like the order by at the end and you calculate the Price and BTC but don't use them in the priceTarget view (you use the already available values from PriceView1). I'd think you left it there to have unique date/symbol, you can use a select DISTINCT to achieve the same result.
I don't know if it is intentional but the BTC and price are calculated from an average in PricesView1 and they are not in PricesView2.
This is my suggestion for the PricesView2:
create view PricesView2 as
select
sq.Date,
newP.Date,
sq.Symbol,
newP.Price as 'NewPrice',
newP.BTC_Dominance as 'NewBTCdominance',
newP.pkdummy
from (
select distinct
Date as 'oldDate',
Symbol,
pkdummy,
pkey
from Prices
having right(pkdummy,2)=22) sq
join Prices newP on
newP.Date=date_add(sq.oldDate, INTERVAL 1 DAY)
and newP.Symbol=sq.Symbol
where right(newP.pkdummy,2)=22
and sq.Date > '2018-11-22'
My understanding of views is that they are comparable to macros in other languages: more like code replacement than pre-computation.
So when you do in priceTarget avg(pv1.Price) considering that pv1.Price is defined as avg(Price) you're averaging an average.
In addition to the changes I suggested above I'd change PricesView2 to calculate the new price and BTC average so the priceTarget view doesn't have to
Last in your priceTarget view you should also group by pv1.pkey in addition to pv1.Date and pv1.symbol.
I would first do some analysis on the queries themselves to find out what is causing the bottle neck i.e. how the tables are being called, how many rows each table returns, which indexes are being used, etc. Simple things like reordering the tables in the FROM clause could help performance. Your queries may lacking an index or two that may greatly improve performance.

Access Row By Row Percent of Total

Sorry if this is a dumb question everyone, but I'm stuck on something that seems pretty simple.
If I create SQL like this:
Weights: [Volume]/2792
I get the row by row percent of the total.
However, there's no way I'm going to hard-code the total, so I really want to setup my query like this:
Weights: [Volume]/sum([Volume])
However, that gives me this.
Can someone show me how to dynamically sum the field 'Weights' so I can get the percent of each record?
Here is the SQL:
SELECT tblOffices.ServiceID, tblOffices.Branch, tblOffices.Volume, [Volume]/Sum([Volume]) AS Weights
FROM tblOffices
GROUP BY tblOffices.ServiceID, tblOffices.Branch, tblOffices.Volume;
Thanks!
What you need is 2 queries or a query with a sub query. if you are not familiar with sub queries, do 2 queries like this:
QRY1
SELECT Sum([VOLUME]) AS [Total Volume]
FROM yourTable
This will give you the sum of all the volumes.
Then build another query, that uses the result of QRY1 as a value for your calculation.
QRY2
SELECT Yourtable.[Volume] AS Vol, YourTable.[Volume]/QRY1.[Total Volume] AS Weight
FROM YourTable,TEST1;
to display as a percentage multiple by 100 or set the format as percentage.

Calculated Fields in MYsql

Excuse the "newbee to modern Databases" question in advance. But have a very simple question.
Given the following table:
ID Cost_Goods Sales_Price
1 100 150
2 75 95
3 500 700
I know that I can Query
SELECT Cost_Goods FROM someTable WHERE ID =2;
Very often I would like to know 'profit' where Profit is calculated as in:
SELECT profit FROM someTable WHERE ID =2;
I know that I can use a function here. But I have some limitations form a whole lot of legacy code. I wish to cmd up with a way to have a sort of "Calculation Field" in the database itself. That would, in addition to the above "profit" query allow me to query like.
SELECT Cost_Goods, profit FROM someTable WHERE ID =2;
Or Even:
SELECT * FROM someTable ORDER BY profit;
Is this possible?
I understand that there are more elegant ways to hand the calculation, but the interface from the application that I am working on/in simply don't allow anything other than avery simple query. I am attempting to shift the calculation over.
You can either alter the table to add in a column, maybe ideal maybe not, but this would require that the new field be kept up-to-date on each Insertion. Not a problem if you have control over this, but can come back and bite you if you think you updated all your INSERTS when in fact you haven't.
I think the preferred way would be to do the calculation in SQL
SELECT Cost_Goods -Sales_Price AS profit FROM someTable ORDER BY profit
Or if you can create a view in the database...
CREATE VIEW sales_profit AS SELECT ID, Cost_Goods, Sales_Price, Cost_Goods -Sales_Price AS profit FROM someTable;
Then you can..
SELECT * FROM sales_profit where ID = 2;
SELECT * FROM sales_profit ORDER BY profit;