How to write an if condition in SQL - mysql

I am trying to calculate the query using if condition, I need to show two values as buying and selling.
SELECT
n.Date as Date,
n.dp_id as DP_ID,
cast(n.emp_no as nchar) as emp_no,
n.holder as HOLDER,
n.add1 as ADD1,
n.add2 as ADD2,
n.add3 as ADD3,
n.add4 as ADD4,
n.pin as PIN,
'emp_table' as rec_type,
(
select s.position from emp_table as s
where s.emp_no = n.emp_no
and s.dp_id = n.dp_id
and s.Date < n.Date
order by s.emp_no,s.Date desc
limit 0,1
) as OPEN_BAL,
n.position as CLOS_BAL
FROM emp_table as n
where true
and n.Date >= '2013-02-01'
and n.Date <= '2013-02-8'
----up to this it was working if condition part not working-----
(select
open_bal,
if(open_bal<clos_bal,clos_bal-open_bal,0) as buying,
if(open_bal>clos_bal,open_bal-clos_bal,0) as selling,
order by buying desc from emp_table
)

You can use CASE in place of the if-condition
SELECT ...,
CASE WHEN open_bal < clos_bal THEN ID ELSE open_bal END AS ColumnName,
...
FROM emp_table

Related

MySql Start and End price (Min,Max) with Inner Joins

I have a table of prices, 2 types. metal 1 and metal 2.
I have succeeded in getting the max, min price for each metal groups by day.
How can i also select the start (first) and end (last) of every day too?
I am nearly there, but struggling on getting these two final prices...
My SQL fiddle with example data:
http://sqlfiddle.com/#!9/ca4867/1
My query so far:
select
highp.metal_price_datetime_IST AS high_price_metal_price_datetime_IST
, highp.metal_price as highest_price
, lowp.report_term
, lowp.metal_id
, lowp.metal_price as lowest_price
, lowp.metal_price_datetime_IST AS low_price_metal_price_datetime_IST
from (select #report_term:=concat(day(metal_price_datetime_IST), ' ', monthname(metal_price_datetime_IST), ' ', year(metal_price_datetime_IST)) as report_term
, metal_price_datetime_IST
, metal_price
, metal_id
, case when #report_term=#old_report_term then #rn1:=#rn1+1 else #rn1:=1 end as rn
, #old_report_term:=#report_term
from metal_prices
cross join (select #rn1:=0, #old_report_term:='') inituservar1
where metal_price_datetime_IST BETWEEN '2018-02-01' AND LAST_DAY('2018-02-01')
order by metal_id, report_term, metal_price asc) lowp
inner join (select #report_term2:=concat(day(metal_price_datetime_IST), ' ', monthname(metal_price_datetime_IST), ' ', year(metal_price_datetime_IST)) as report_term
, metal_price_datetime_IST
, metal_price
, metal_id
, case when #report_term2=#old_report_term2 then #rn2:=#rn2+1 else #rn2:=1 end as rn
, #old_report_term2:=#report_term2
from metal_prices
cross join (select #rn2:=0, #old_report_term2:='') inituservar1
where metal_price_datetime_IST BETWEEN '2018-02-01' AND LAST_DAY('2018-02-01')
order by metal_id, report_term, metal_price desc) highp
on lowp.rn=highp.rn
and lowp.metal_id = highp.metal_id
and lowp.report_term = highp.report_term
and lowp.rn = 1
and (lowp.metal_id = 1 or lowp.metal_id = 2)
order by lowp.metal_price_datetime_IST DESC
The query you have in your fiddle seems too complex for what needs to be done. I have refactored and rewritten the query. Basically, the query is split in two parts. First one maxminprice determines the max and min price for each day for each metal. Fairly straight forward. The second part firstlastprice is a bit more complex. It finds out the max and min time stamps for each metal for each day. Then joins back to the main table to get the values for those time stamps. The case statement there is to merge the results for max and min (first and last) time so we don't have to do the query twice.
SELECT maxminprice.metal_id,
maxminprice.metal_price_datetime,
maxminprice.max_price,
maxminprice.min_price,
firstlastprice.first_price,
firstlastprice.last_price
FROM (SELECT metal_id,
DATE(metal_price_datetime) metal_price_datetime,
MAX(metal_price) max_price,
MIN(metal_price) min_price
FROM metal_prices
GROUP BY metal_id,
DATE(metal_price_datetime)
ORDER BY metal_id,
DATE(metal_price_datetime)) maxminprice
INNER JOIN (SELECT mp.metal_id,
day_range.metal_price_datetimefl,
SUM(CASE
WHEN TIME(mp.metal_price_datetime) = first_time
THEN
mp.metal_price
ELSE NULL
END) first_price,
SUM(CASE
WHEN TIME(mp.metal_price_datetime) = last_time
THEN
mp.metal_price
ELSE NULL
END) last_price
FROM metal_prices mp
INNER JOIN (SELECT metal_id,
DATE(metal_price_datetime)
metal_price_datetimefl,
MAX(TIME(metal_price_datetime))
last_time,
MIN(TIME(metal_price_datetime))
first_time
FROM metal_prices
GROUP BY metal_id,
DATE(metal_price_datetime))
day_range
ON mp.metal_id = day_range.metal_id
AND DATE(mp.metal_price_datetime) =
day_range.metal_price_datetimefl
AND TIME(mp.metal_price_datetime) IN
( last_time, first_time )
GROUP BY mp.metal_id,
day_range.metal_price_datetimefl) firstlastprice
ON maxminprice.metal_id = firstlastprice.metal_id
AND maxminprice.metal_price_datetime =
firstlastprice.metal_price_datetimefl

SQL Queries to analyse Employee Database

I am looking for queries, using which I can analyze a general employee database. This is for Data Analysis.
Tried this for monthly employee trend
SELECT
dt.FullDateAlternateKey as 'Date'
, count(1) as ActiveCount
FROM DimDate dt
LEFT JOIN (SELECT 'Active' as 'EmpStatus', * FROM DimEmployee) emp
-- regular active employees
ON (dt.FullDateAlternateKey between emp.StartDate and ISNULL(emp.EndDate,'9999-12-31'))
WHERE
dt.FullDateAlternateKey = EOMONTH(dt.FullDateAlternateKey)
GROUP BY
dt.FullDateAlternateKey
ORDER BY
1;
also found CTE use for finding employee hierarchy
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
AS
(
-- Anchor member definition
SELECT e.ParentEmployeeKey, e.EmployeeKey, e.Title, e.DepartmentName,
0 AS Level
FROM DimEmployee AS e
WHERE e.ParentEmployeeKey IS NULL
UNION ALL
-- Recursive member definition
SELECT e.ParentEmployeeKey, e.EmployeeKey, e.Title, e.DepartmentName,
Level + 1
FROM DimEmployee AS e
INNER JOIN DirectReports AS d
ON e.ParentEmployeeKey = d.EmployeeID
)
-- Statement that executes the CTE
SELECT ManagerID, EmployeeID, Title, DeptID, Level
FROM DirectReports
WHERE DeptID = 'Information Services' OR Level = 0
also, some good queries to analyze the sales data
-- Show each sales average for Group, Country, and Region all in one query
SELECT DISTINCT
t.SalesTerritoryGroup
, t.SalesTerritoryCountry
, t.SalesTerritoryRegion
, AVG(s.SalesAmount) OVER(PARTITION BY t.SalesTerritoryGroup ) as 'GroupAvgSales'
, AVG(s.SalesAmount) OVER(PARTITION BY t.SalesTerritoryCountry ) as 'CountryAvgSales'
, AVG(s.SalesAmount) OVER(PARTITION BY t.SalesTerritoryRegion ) as 'RegionAvgSales'
FROM FactInternetSales s
JOIN DimSalesTerritory t ON
s.SalesTerritoryKey = t.SalesTerritoryKey
WHERE
YEAR(s.OrderDate) = 2013
ORDER BY
1,2,3
Use additional aggregations to understand more about product sales such as the distribution of sales etc..
SELECT
cat.EnglishProductCategoryName 'Category'
, sub.EnglishProductSubcategoryName 'SubCategory'
, count(1) 'Count' -- How many sales where there?
, sum(s.SalesAmount) 'Sales' -- How much sales did we have?
, avg(s.SalesAmount) 'Avg_SalesAmount' -- What was the Avg sale amount?
, min(s.SalesAmount) 'Min_SaleAmount' -- What was the Min sale amount?
, max(s.SalesAmount) 'Max_SaleAmount' -- What was the Max sale amount
FROM FactInternetSales s
LEFT JOIN DimProduct p ON s.ProductKey = p.ProductKey
LEFT JOIN DimProductSubcategory sub ON p.ProductSubcategoryKey = sub.ProductSubcategoryKey
LEFT JOIN DimProductCategory cat ON sub.ProductCategoryKey = cat.ProductCategoryKey
-- must use group by in order for aggregation to work properly
GROUP BY
cat.EnglishProductCategoryName -- column aliases aren't allowed
, sub.EnglishProductSubcategoryName
ORDER BY
cat.EnglishProductCategoryName
, sub.EnglishProductSubcategoryName
-- Calculate the customer acquisition funnel
SELECT
c.FirstName
, c.LastName
, c.DateFirstPurchase
, DATEDIFF(d,c.DateFirstPurchase,getdate()) as 'DaysSinceFirstPurchase' -- How long have they been a customer?
FROM DimCustomer c
ORDER BY 3 DESC
-- Calculate a Monthly average of customer tenure
SELECT
EOMONTH(c.DateFirstPurchase) as 'MonthOfFirstPurchase' -- What month did they become a customer?
, DATEDIFF(d,EOMONTH(c.DateFirstPurchase),getdate()) as 'DaysSinceFirstPurchase' -- How long have they been a customer?
, COUNT(1) as 'CustomerCount' -- How manY customers are there for this month?
FROM DimCustomer c
GROUP BY EOMONTH(c.DateFirstPurchase)
ORDER BY 2 DESC
-- Show the top product Sub Categories for each year
SELECT
count(DISTINCT s.SalesOrderNumber) 'OrderCount' -- use 1 instead of a field for faster performance
, RANK() OVER (PARTITION BY YEAR(s.OrderDate) ORDER BY sum(s.SalesAmount) DESC) 'SalesRank'
, sum(s.SalesAmount) 'TotalSales'
, cat.EnglishProductCategoryName 'Category'
, sub.EnglishProductSubcategoryName 'SubCategory'
, YEAR(s.OrderDate) 'Year'
FROM FactInternetSales s
INNER JOIN DimProduct p ON s.ProductKey = p.ProductKey
INNER JOIN DimProductSubcategory sub ON p.ProductSubcategoryKey = sub.ProductSubcategoryKey
INNER JOIN DimProductCategory cat ON sub.ProductCategoryKey = cat.ProductCategoryKey
-- must use group by in order for aggregation to work properly
GROUP BY
cat.EnglishProductCategoryName -- column aliases aren't allowed
, sub.EnglishProductSubcategoryName
, YEAR(s.OrderDate)
ORDER BY YEAR(s.OrderDate), SUM(s.SalesAmount) DESC;
-- first, create weekly sales totals
SELECT SUM(s.SalesAmount) 'WeeklySales'
, DATEPART(ww, s.OrderDate) as 'WeekNum'
FROM FactInternetSales s
WHERE YEAR(s.OrderDate) = 2013
GROUP BY
DATEPART(ww, s.OrderDate)
ORDER BY
DATEPART(ww, s.OrderDate) ASC
-- use that subquery as our source and calculate the moving average
SELECT
AVG(WeeklySales) OVER (ORDER BY WeekNum ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as AvgSales
, WeeklySales as 'TotalSales'
, WeekNum
FROM (
SELECT SUM(s.SalesAmount) 'WeeklySales'
, DATEPART(ww, s.OrderDate) as 'WeekNum'
FROM FactInternetSales s
WHERE YEAR(s.OrderDate) = 2013
GROUP BY
DATEPART(ww, s.OrderDate)
) AS s
GROUP BY
WeekNum, WeeklySales
ORDER BY
WeekNum ASC
-- Running Total
SELECT
SUM(MonthlySales) OVER (PARTITION BY SalesYear ORDER BY SalesMonth ROWS UNBOUNDED PRECEDING) as YTDSales
, MonthlySales as 'MonthlySales'
, SalesYear
, SalesMonth
FROM (
SELECT SUM(s.SalesAmount) 'MonthlySales'
, MONTH(s.OrderDate) as 'SalesMonth'
, year(s.OrderDate) as 'SalesYear'
FROM FactInternetSales s
GROUP BY
MONTH(s.OrderDate)
, year(s.OrderDate)
) AS s
GROUP BY
SalesMonth, SalesYear, MonthlySales
ORDER BY
SalesYear, SalesMonth ASC
-- Get Prev Year Sales
WITH MonthlySales (YearNum, MonthNum, Sales)
AS
(
SELECT d.CalendarYear, d.MonthNumberOfYear, SUM(s.SalesAmount)
FROM DimDate d
JOIN FactInternetSales s ON d.DateKey = s.OrderDateKey
GROUP BY d.CalendarYear, d.MonthNumberOfYear
)
-- Get Current Year and join to CTE for previous year
SELECT
d.CalendarYear
, d.MonthNumberOfYear
, ms.Sales PrevSales
, SUM(s.SalesAmount) CurrentSales
FROM DimDate d
JOIN FactInternetSales s ON d.DateKey = s.OrderDateKey
JOIN MonthlySales ms ON
d.CalendarYear-1 = ms.YearNum AND
d.MonthNumberOfYear = ms.MonthNum
GROUP BY
d.CalendarYear
, d.MonthNumberOfYear
, ms.Sales
ORDER BY
1 DESC, 2 DESC
-- Now calculate the % change Year over Year
WITH MonthlySales (YearNum, MonthNum, Sales)
AS
(
SELECT d.CalendarYear, d.MonthNumberOfYear, SUM(s.SalesAmount)
FROM DimDate d
JOIN FactInternetSales s ON d.DateKey = s.OrderDateKey
GROUP BY d.CalendarYear, d.MonthNumberOfYear
)
-- Get Current Year and join to CTE for previous year
SELECT
d.CalendarYear
, d.MonthNumberOfYear
, ms.Sales PrevSales
, SUM(s.SalesAmount) CurrentSales
, (SUM(s.SalesAmount) - ms.Sales) / SUM(s.SalesAmount) 'PctGrowth'
FROM DimDate d
JOIN FactInternetSales s ON d.DateKey = s.OrderDateKey
JOIN MonthlySales ms ON
d.CalendarYear-1 = ms.YearNum AND
d.MonthNumberOfYear = ms.MonthNum
GROUP BY
d.CalendarYear
, d.MonthNumberOfYear
, ms.Sales
ORDER BY
1 DESC, 2 DESC

MySQL SELECT query returns wrong result

I have a table orders(c_id(INT),o_date(DATE),o_price(INT)). I have to find out the id and MAX value of total price for each id in the current year. The table is as follows:
c_id o_date o_price
1 2017-08-27 30
2 2017-05-25 100
2 2017-05-02 20
1 2017-02-23 80
3 2017-01-26 60
4 2016-04-22 10
2 2016-03-15 5
1 2015-09-01 1
I code as follow:
SELECT c_id, MAX(m_price)
FROM ( SELECT c_id, SUM(o_price) AS
m_price FROM orders WHERE
( YEAR(o_date) = YEAR(curdate()) )
GROUP
> BY c_id )AS T
the MAX value return right but the id is not fit with that MAX value. Has anyone a suggestion for my situation? Thank you in advance!!
If there is guaranteed to be only one c_id which has the largest sum, or you don't mind returning only one result in the event of a tie, then you can just LIMIT 1 along with an ORDER BY to get the max result.
SELECT
c_id,
SUM(o_price) AS m_price
FROM orders
WHERE
YEAR(o_date) = YEAR(CURDATE())
GROUP BY c_id
ORDER BY m_price DESC
LIMIT 1
If there could be a tie for maximum sum, then there are workarounds which are slightly uglier, e.g.
SELECT
c_id,
SUM(o_price) AS m_price
FROM orders
WHERE
YEAR(o_date) = YEAR(CURDATE()) AND
GROUP BY c_id
HAVING SUM(o_price) =
(
SELECT MAX(t.m_price)
FROM
(SELECT SUM(o_price) AS m_price FROM orders
WHERE YEAR(o_date) = YEAR(CURDATE()) GROUP BY c_id) t
)
The above query is a bit verbose, and in other databases we would use analytic functions to handle this. But since MySQL does not support these out of the box we need to use another way.
If you have multiple prices, the following is slightly simpler than Tim's code:
SELECT o.c_id, SUM(o.o_price) AS m_price
FROM orders o
WHERE YEAR(o.o_date) = YEAR(CURDATE())
GROUP BY o.c_id
HAVING m_price = (SELECT SUM(o2.o_price)
FROM orders o2
WHERE YEAR(o2.o_date) = YEAR(CURDATE()
GROUP BY c_id
ORDER BY SUM(o2.o_price) DESC
LIMIT 1
);
In practice, using variables might be faster.
SELECT o.*
FROM (SELECT o.*, #maxp := GREATEST(#maxp, m_price)
FROM (SELECT o.c_id, SUM(o.o_price) AS m_price
FROM orders o
WHERE YEAR(o.o_date) = YEAR(CURDATE())
GROUP BY o.c_id
) o CROSS JOIN
(SELECT #maxp := 0) params
) o
WHERE m_price = #maxp;
Adjust your query as
SELECT c_id, MAX(m_price)
FROM ( SELECT c_id, SUM(o_price) AS
m_price FROM orders WHERE
YEAR(o_date) = YEAR(curdate())
)
GROUP BY c_id
My solution for this is:
set #p1 = (SELECT MAX(m_price) FROM
(
SELECT c_id, SUM(o_price) AS m_price FROM orders WHERE
(
YEAR(o_date) = YEAR(curdate())
) GROUP BY c_id
) as T
);
SELECT c_id from (
SELECT c_id, sum(o_price) as m_price from orders WHERE (
YEAR(o_date) = YEAR(curdate())
)group by c_id
) as a WHERE m_price = #p1

Better optimized SELECT SQL query for 50,000+ records

I have a query which works great for 1000 records or less but now I need to optimize it for 50,000+ records and when I run it on that it just stalls...
Here is my code:
SELECT
b1.account_num,b1.effective_date as ed1,b1.amount as am1,
b2.effective_date as ed2,b2.amount as am2
FROM bill b1
left join bill b2 on (b1.account_num=b2.account_num)
where b1.effective_date = (select max(effective_date) from bill where account_num = b1.account_num)
and (b2.effective_date = (select max(effective_date) from bill where account_num = b1.account_num and effective_date < (select max(effective_date) from bill where account_num = b1.account_num)) or b2.effective_date is null)
ORDER BY b1.effective_date DESC
My objective is to get the latest two effective dates and amounts from one table with many records.
Here is a working answer from your SQL-Fiddle baseline
First, the inner preQuery gets the max date per account. That is then joined to the bill table per account AND the effective date is less than the max already detected.
That is then joined to each respective bill for their amounts.
select
FB1.account_num,
FB1.effective_date as ed1,
FB1.amount as am1,
FB2.effective_date as ed2,
FB2.amount as am2
from
( select
pq1.account_num,
pq1.latestBill,
max( b2.effective_date ) as secondLastBill
from
( SELECT
b1.account_num,
max( b1.effective_date ) latestBill
from
bill b1
group by
b1.account_num ) pq1
LEFT JOIN bill b2
on pq1.account_num = b2.account_num
AND b2.effective_date < pq1.latestBill
group by
pq1.account_num ) Final
JOIN Bill FB1
on Final.Account_Num = FB1.Account_Num
AND Final.LatestBill = FB1.Effective_Date
LEFT JOIN Bill FB2
on Final.Account_Num = FB2.Account_Num
AND Final.secondLastBill = FB2.Effective_Date
ORDER BY
Final.latestBill DESC
In mysql , window analytic function like row_number is not there, so we can simulate the same using variables.
The good thing is, the table is scanned only once with this approach.
A row_number is assigned to each partition which is divided based on ( account number, effective date ) and only 2 rows are selected from each partition.
select account_num,
max(case when row_number =1 then effective_date end) as ed1,
max(case when row_number =1 then amount end) as am1,
max(case when row_number =2 then effective_date end) as ed2,
max(case when row_number =2 then amount end )as am2
from (
select account_num, effective_date, amount,
#num := if(#prevacct= account_num , #num + 1, 1) as row_number,
#prevacct := account_num as dummy
from bill, (select #num:=0, #prevacct := '' ) as var
order by account_num , effective_date desc
)T
where row_number <=2
group by account_num

Find 3rd Greatest Value with MySQL GROUP BY

I have a table with 3 columns:
Name department salary
How can I determine using one query to find 3rd highest salary in each department?
One way is to LIMIT a correlated subquery, but it's not especially efficient:
SELECT department, (
SELECT salary
FROM my_table t2
WHERE t2.department = t1.department
ORDER BY salary DESC
LIMIT 2, 1
)
FROM my_table t1
GROUP BY department
In addition to eggyal's excellent answer, here's a query that will give you the names, too, of those that have salary equal to the third (in each department):
SELECT
t.name, t.department, t.salary AS third_salary
FROM
( SELECT DISTINCT department
FROM tableX
) AS d
JOIN
tableX AS t
ON t.department = d.department
AND t.salary =
( SELECT tt.salary -- notice that this
FROM tableX AS tt -- subquery is
WHERE tt.department = d.department -- exactly the same as
ORDER BY tt.salary DESC -- the one in
LIMIT 1 OFFSET 2 -- #eggyal's answer
) ;
This RANK question is similar to this one:
MySQL, Get users rank
I you can thy this:
SELECT s.*,
(
SELECT COUNT(*)
FROM salaries si
WHERE si.salary >= s.salary AND si.department = s.department
) AS rank
FROM salaries s
WHERE s.rank = 3
Try this:
SELECT name, department, salary
FROM (SELECT name, department, salary, IF(#dept=(#dept:=department), #auto:=#auto+1, #auto:=1) indx
FROM employee e, (SELECT #dept:=0, #auto:=1) A
ORDER BY department, salary DESC ) AS A
WHERE indx = 3;