How to avoid conditional multiple SELECT in a query - mysql

I have this simple SQL
SELECT
(CASE
WHEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer) IS NOT NULL
THEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer)
ELSE Customer
END) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;
My question is how can I avoid make two same SELECT statements: one as condition and other as a value selection. I know that I can create a temporary variable, but this forces me to use stored procedures or temporary tables. Is there a simple way to avoid this?

You could use IFNULL like this:
IFNULL((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Or COALESCE like this:
COALESCE((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Reference:
COALESCE(value,...)
IFNULL(expr1,expr2)

Try this
SELECT
IsNull(Name,Customer)
AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;

You can rewrite your query by using join
SELECT
COALESCE(c.Name,Customer) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Programs.`Name` AS ProgramName
FROM ProgramsRun
LEFT JOIN Programs ON ProgramsRun.Programs_id = Programs.Programs_id
LEFT JOIN Customers c ON c.Customers_id = Customer;

Related

Filtering out a row with a null value

How would I filter out a row with a null value?
SELECT country_table.country as country,
(SELECT TRUNCATE(AVG(news_guard_score), 3) FROM news WHERE news.country_id = country_table.country_id) as 'avg_news_score'
FROM country_table
-- WHERE country_table.country_id IS NOT NULL
GROUP BY country;
It does not work with the commented code
How it looks like
I need it without the final row
Have you tried:
SELECT country_table.country as country,
(SELECT TRUNCATE(AVG(news_guard_score), 3) FROM news WHERE (news.country_id = country_table.country_id AND country_table.country_id IS NOT NULL)) as 'avg_news_score'
FROM country_table
GROUP BY country;
It sounds like you need a HAVING clause:
SELECT c.country, TRUNCATE(AVG(n.news_guard_score), 3)
FROM country_table c
INNER JOIN news n ON n.country_id = c.country_id
GROUP BY c.country
HAVING AVG(n.news_guard_score) IS NOT NULL;
Note: I have rewritten your query to use a direct inner join, rather than the correlated subquery which you were using (and which would probably not perform too well).

Cleaner SQL Query

I have 2 tables that are joined using foreign key, example: budget_items table column "id" and transactions tables column "parent_id"
Each table has some similar column names such as name and date for example. So when I query the table I'm pulling the data out as follows:
SELECT
budget_items.id AS BI_id,
budget_items.owner_id as BI_owner,
budget_items.name AS BI_name,
budget_items.date AS BI_date,
budget_items.amount AS BI_amount,
budget_items.type AS BI_type,
transactions.id as TRANS_id,
transactions.parent_id as TRANS_parent,
transactions.owner_id as TRANS_owner,
transactions.amount as TRANS_amount,
transactions.date as TRANS_date,
transactions.type as TRANS_type,
transactions.processed as TRANS_processed,
transactions.name AS TRANS_name
FROM
myezbudget.budget_items budget_items,
myezbudget.transactions transactions
WHERE
budget_items.id = transactions.parent_id AND
budget_items.owner_id = ?`, [req.user.id]
My question would be, is there a better way to do this with a more concise query? I tried with a few different types of join statements but couldn't get it to output the way I liked. I'm by no means a SQL expert and would appreciate any suggestions or guidance.
I would:
Use modern JOIN syntax.
Use shorter table aliases.
The query should look like:
SELECT
i.id AS BI_id,
i.owner_id as BI_owner,
i.name AS BI_name,
i.date AS BI_date,
i.amount AS BI_amount,
i.type AS BI_type,
t.id as TRANS_id,
t.parent_id as TRANS_parent,
t.owner_id as TRANS_owner,
t.amount as TRANS_amount,
t.date as TRANS_date,
t.type as TRANS_type,
t.processed as TRANS_processed,
t.name AS TRANS_name
FROM myezbudget.budget_items i
JOIN myezbudget.transactions t on i.id = t.parent_id
WHERE i.owner_id = ?
Yes you could use an inner join instead of selecting two tables in the FROM statement and linking them together in the WHERE by doing it like this:
SELECT
budget_items.id AS BI_id,
budget_items.owner_id as BI_owner,
budget_items.name AS BI_name,
budget_items.date AS BI_date,
budget_items.amount AS BI_amount,
budget_items.type AS BI_type,
transactions.id as TRANS_id,
transactions.parent_id as TRANS_parent,
transactions.owner_id as TRANS_owner,
transactions.amount as TRANS_amount,
transactions.date as TRANS_date,
transactions.type as TRANS_type,
transactions.processed as TRANS_processed,
transactions.name AS TRANS_name
FROM
myezbudget.budget_items budget_items,
INNER JOIN myezbudget.transactions AS transactions ON budget_items.id = transactions.parent_id
WHERE
budget_items.owner_id = ?`, [req.user.id]
You could always try shortening the table aliases as well to make them more compact but if you want them descriptive then that's fine. Also when the column only exists in one table then you don't have to specify the table name all of the time, however I would say this is personal preference.

Using From (subquery) table in another subquery

So I have the following :
SELECT bt.CompanyName,
AcceptedTable.Count AS AcceptedCount,
CompletedTable.Count AS CompletedCount,
SkippedTable.Count AS SkippedCount,
TotalTable.Count AS TotalCount
FROM (
SELECT uic.*, uic.Id AS UCId, ic.*, ic.Id AS CompanyId, ic.Name AS CompanyName
FROM UserChallenges iuc
JOIN Users iu ON iuc.UserId = iu.Id
JOIN Companies ic ON ic.Id = iu.CompanyId) bt
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
GROUP BY CompanyId) TotalTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE AcceptedAt IS NOT NULL
GROUP BY CompanyId) AcceptedTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE DoneStatus IN (1,6)
GROUP BY CompanyId) CompletedTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE DoneStatus IN (4)
GROUP BY CompanyId) SkippedTable ON SkippedTable.CompanyId = bt.CompanyId
GROUP BY bt.CompanyName
The goal is to compute Counts on the same set of data with different WHERE clauses.
I tried the above approach as it simplifies a bit the query instead of having to do JOINs again in each sub queries.
But it seems like it's not possible, it's Common table expression and is not available in mysql 5.7.
What would be a better way to implement this ?
There must be a cleaner way than this super long query.
I could use a temporary table too, although I'm not sure it's a good idea in a View.
You cannot refer another Derived Table alias within a different Derived Table's FROM clause, because it is not accessible at that level.
Also, I feel that you can rather solve this problem using Conditional Aggregation:
SELECT bt.CompanyName,
COUNT(CASE WHEN bt.AcceptedAt IS NOT NULL THEN bt.UCId END) AS AcceptedCount,
COUNT(CASE WHEN bt.DoneStatus IN (1,6) THEN bt.UCId END) AS CompletedCount,
COUNT(CASE WHEN bt.DoneStatus IN (4) THEN bt.UCId END) AS SkippedCount,
COUNT(bt.UCId) AS TotalCount
FROM (
SELECT uic.*,
uic.Id AS UCId,
ic.*,
ic.Id AS CompanyId,
ic.Name AS CompanyName
FROM UserChallenges iuc
JOIN Users iu ON iuc.UserId = iu.Id
JOIN Companies ic ON ic.Id = iu.CompanyId) bt
GROUP BY bt.CompanyId, bt.CompanyName

SQL sort data based on different columns

I need some help with an SQL problem. I'm not sure it's possible to do, but i have this table:
Obtained from this SQL statement:
SELECT distinct Account.Box, Account.Name, Account.Currency, Account.LastUpdated, Account.LastUpdatedBy, Transactions.Totals
FROM Account
LEFT JOIN Transactions
ON Account.AccountGUID = Transactions.AccountGUID
What i would like to end up with is this result:
So basically comparing the Totals for each groups of Name or Currency, to obtain the highest Totals for each group. (NULL values are to be replaced by zeroes).
You seem to just want a group by:
SELECT a.Box, a.Name, a.Currency, a.LastUpdated, a.LastUpdatedBy,
MAX(t.Totals) as Totals
FROM Account a LEFT JOIN
Transactions t
ON a.AccountGUID = t.AccountGUID
GROUP BY a.Box, a.Name, a.Currency, a.LastUpdated, a.LastUpdatedBy;
Note: I also added table aliases to make the query easier to understand.
Try with this following one ,hope you will get the result what you are expecting.
select * from
(SELECT distinct Account.Box, Account.Name, Account.Currency, Account.LastUpdated, Account.LastUpdatedBy, Transactions.Totals as Totals
FROM Account
LEFT JOIN Transactions
ON Account.AccountGUID = Transactions.AccountGUID) as p
where p.Totals = (select max(totals) from transactions)
group by Box, Name, Currency, LastUpdated, LastUpdatedBy ;
(or)
use the following one also
SELECT a.Box, a.Name, a.Currency, a.LastUpdated, a.LastUpdatedBy,
MAX(t.Totals) as Totals
FROM Account a LEFT JOIN
Transactions t
ON a.AccountGUID = t.AccountGUID
GROUP BY a.Box, a.Name, a.Currency, a.LastUpdated, a.LastUpdatedBy;
Thanks.

How to give alias to results returned after inner join in mySQL

I need to do a correlated SQL Query and for that purpose i need to provide an alias to outer query which in which I perform an inner join. I am not able to do the alias
SELECT DISTINCT(name)
FROM PERSON
INNER JOIN M_DIRECTOR AS dira
ON (dira.PID = M_DIRECTOR.PID) as dira
WHERE 9 > (
SELECT COUNT(MID) FROM M_DIRECTOR WHERE name = dira.name
) ;
I didn't really understand what you want to do, but I guess
select
distinct p.name,
count(d.MID) cnt
from
hindi2_PERSON p
inner join
hindi2_M_DIRECTOR d
on
p.PID = d.PID
group by
p.name
having count(d.MID) > 9
;
would do what you want
I dont know what you are asking and what you mean by make an alias to an eniter result ?
but you are doing
select distinct(name) as othername
which is you are selecting name and you are giving here othername as an alias
then you retrieve it in result
$row['othername']
There's still something missing. From what you write, there is a field name in the M_DIRECTOR table?
Please show all the tables and attributes involved, use an SQL Fiddle to prepare an example.
SELECT DISTINCT(name)
FROM PERSON as p
INNER JOIN (
SELECT COUNT(MID), PID FROM M_DIRECTOR WHERE name = dira.name
) as d
ON (p.PID = d.PID) ;