Add WHERE clause to single column - mysql

I have a query which I am working on, basically I have 3 columns:
Code: The users Code
Orders Taken: The orders which have been taken
Orders Taken From an External Call
Basically I just need a way to only allow Orders From External Calls to have a where clauses, when I add in my WHERE clause, it does it for both tables.
My query so far:
SELECT T_Temp_RestrictedDiaryCalls.AccreditedDomainCode,
Count(T_Temp_RestrictedProductSalesHistory.CustomerCode) AS [Orders Taken],
Count(T_Temp_RestrictedProductSalesHistory.CustomerCode) AS [Orders From External Calls]
FROM T_Temp_RestrictedDiaryCalls
INNER JOIN T_Temp_RestrictedProductSalesHistory
ON (T_Temp_RestrictedDiaryCalls.CustomerCode = T_Temp_RestrictedProductSalesHistory.CustomerCode)
AND (T_Temp_RestrictedDiaryCalls.CallDate = T_Temp_RestrictedProductSalesHistory.EntryDate)
GROUP BY T_Temp_RestrictedDiaryCalls.AccreditedDomainCode;
Any help will be greatly appreciated.

If I've got it right you should use CASE statement in the [Orders From External Calls] definition:
SELECT T_Temp_RestrictedDiaryCalls.AccreditedDomainCode,
Count(T_Temp_RestrictedProductSalesHistory.CustomerCode) AS [Orders Taken],
sum( CASE WHEN <Your condition here>
THEN 1
ELSE 0
END) AS [Orders From External Calls]
FROM T_Temp_RestrictedDiaryCalls
INNER JOIN T_Temp_RestrictedProductSalesHistory
ON (T_Temp_RestrictedDiaryCalls.CustomerCode = T_Temp_RestrictedProductSalesHistory.CustomerCode)
AND (T_Temp_RestrictedDiaryCalls.CallDate = T_Temp_RestrictedProductSalesHistory.EntryDate)
GROUP BY T_Temp_RestrictedDiaryCalls.AccreditedDomainCode;

Based on what you said on comments one column might have the count of all rows, and other column just show value on specify case.
So:
select table1.a,count(table1.b),
(select count(table2.c) from table2 where "where clause")
from table1

If I understand you correctly, you want both of those Orders columns to count data from the same table, but with different restrictions on which records from the table are counted and which are not. So do something like:
SELECT T_Temp_RestrictedDiaryCalls.AccreditedDomainCode,
Count(SalesHistoryForAllOrders.CustomerCode) AS [Orders Taken],
Count(SalesHistoryForExternalOrders.CustomerCode) AS [Orders From External Calls]
FROM T_Temp_RestrictedDiaryCalls
INNER JOIN T_Temp_RestrictedProductSalesHistory SalesHistoryForAllOrders
ON (T_Temp_RestrictedDiaryCalls.CustomerCode = SalesHistoryForAllOrders.CustomerCode)
AND (T_Temp_RestrictedDiaryCalls.CallDate = SalesHistoryForAllOrders.EntryDate)
INNER JOIN T_Temp_RestrictedProductSalesHistory SalesHistoryForExternalOrders
ON (T_Temp_RestrictedDiaryCalls.CustomerCode = SalesHistoryForExternalOrders.CustomerCode)
AND (T_Temp_RestrictedDiaryCalls.CallDate = SalesHistoryForExternalOrders.EntryDate)
GROUP BY T_Temp_RestrictedDiaryCalls.AccreditedDomainCode;
Explanation:
By INNER JOINing the table two times and aliasing it each time, we now have two separate representations of the table to work with (called SalesHistoryForAllOrders and SalesHistoryForExternalOrders).
You are then free to place a WHERE clause that refers to either one of them separately, and it will ONLY affect that column, not the other one. So either do:
WHERE SalesHistoryForExternalOrders.IsAnExternalOrder > 0
or just add an additional AND to the second INNER JOIN:
AND SalesHistoryForExternalOrders.IsAnExternalOrder > 0
, the result is the same: that clause will only be applied the External Calls column.

Related

MySQL DISTINCT returning not so distinct results

Good day,
I have a small issue with MySQL Distinct.
Trying the following query in my system :
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`, `bookingcomment_message` FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791
The point is that there are bookings like 29791 that have many comments added.
Let's say 10. Then when running the above query I see 10 results instead of one.
And that's not the way DISTINCT supposes to work.
I simply want to know if there are any comments. If the comment ID is not 0 then there is a comment. Of course I can add COUNT(blabla) as comment_number but that's a whole different story. For me now I'd like just to have this syntax right.
You may try aggregating here, to find which bookings have at least a single comment associated with them:
SELECT
b.booking_id,
b.booking_ticket,
b.booking_price
FROM mysystem_booking b
LEFT JOIN mysystem_bookingcomment bc
ON b.booking_id = bc.bookingcomment_link
WHERE
b.booking_id = 29791
GROUP BY
b.booking_id
HAVING
COUNT(bc.bookingcomment_link) > 0;
Note that depending on your MySQL server mode, you might have to also add the booking_ticket and booking_price columns to the GROUP BY clause to get the above query to run.
You can try below - using a case when expression
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`,
case when `bookingcomment_message`<>'0' then 'No' else 'Yes' end as comments
FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791

Joining and filtering one-to-many relationship

I need some help about optimal structuring of SQL query. I have model like this:
I'm trying some kind of join between tables NON_NATURAL_PERSON and NNP_NAME. Because I have many names in table NNP_NAME for one person I can't do one-to-one SELECT * from NON_NATURAL_PERSON inner join NNP_NAME etc. That way I'll get extra rows for every name one person has.
Data in tables:
How to extend this query to get rows marked red on picture shown below? My wannabe query criteria is: Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.
SELECT nnp.ID, name.NAME, name.TYPE
FROM NON_NATURAL_PERSON nnp
INNER JOIN NNP_NAME name ON (name.NON_NATURAL_PERSON = nnp.ID)
If type is spelled exactly as it's written (typeA, typeB, typeC) then you can use MIN() function:
SELECT NON_NATURAL_PERSON, MIN(type) AS min_type
FROM NNP_NAME
GROUP BY NON_NATURAL_PERSON
if you also want the username you can use this query:
SELECT
n1.NON_NATURAL_PERSON AS ID,
n1.Name,
n1.Type
FROM
NNP_NAME n1 LEFT JOIN NNP_NAME n2
ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
AND n1.Type > n2.type
WHERE
n2.type IS NULL
Please see this fiddle. If Types are not literally sorted, change this line:
AND n1.Type > n2.type
with this:
AND FIELD(n1.Type, 'TypeA', 'TypeB', 'TypeC') >
FIELD(n2.type, 'TypeA', 'TypeB', 'TypeC')
MySQL FIELD(str, str1, str2, ...) function returns the index (position) of str in the str1, str2, ... list, and 0 if str is not found. You want to get the "first" record, ordered by type, for every NON_NATURAL_PERSON. There are multiple ways to get this info, I chose a self join:
ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
AND n1.Type > n2.type -- or filed function
with the WHERE condition:
WHERE n2.type IS NULL
this will return all rows where the join didn't succeed - the join won't succeed when there is not n2.type that is less than n1.type - it will return the first record.
Edit
If you want a platform independent solution, avoiding the creation of new tables, you could use CASE WHEN, just change
AND n1.Type > n2.Type
with
AND
CASE
WHEN n1.Type='TypeA' THEN 1
WHEN n1.Type='TypeB' THEN 2
WHEN n1.Type='TypeC' THEN 3
END
>
CASE
WHEN n2.Type='TypeA' THEN 1
WHEN n2.Type='TypeB' THEN 2
WHEN n2.Type='TypeC' THEN 3
END
There is a piece of information missing. You say:
Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.
But you do not indicate why you prefer typeA over typeB. This information is not included in your data.
In the answer of #fthiella, either lexicographical is assumed, or an arbitrary order is given using FIELD. This is also the reason why two joins with the table nnp_name is necessary.
You can solve this problem by adding a table name_type (id, name, order) and changing the type column to contain the id. This will allow you to add the missing information in a clean way.
With an additional join with this new table, you will be able get the preferred nnp_name for each row.

MySQL Select within another select

I have a query as follows
select
Sum(If(departments.vat, If(weeklytransactions.weekendingdate Between
'2011-01-04' And '2099-12-31', weeklytransactions.takings / 1.2,
If(weeklytransactions.weekendingdate Between '2008-11-30' And '2010-01-01',
weeklytransactions.takings / 1.15, weeklytransactions.takings / 1.175)),
weeklytransactions.takings)) As Total,
weeklytransactions.weekendingdate,......
and another that returns a vat rate as follows
select format(Max(Distinct vat_rates.Vat_Rate),3) From vat_rates Where
vat_rates.Vat_From <= '2011-01-03'
I want to replace the hard coded if statement with the lower query, replacing the date in the lower query with weeklytransactions.weekendingdate.
After Kevin's comments, here is the full query I'm trying to get to work;
Select Max(vat_rates.vat_rate) As r,
If(departments.vat, weeklytransactions.takings / r, weeklytransactions.takings) As Total,
weeklytransactions.weekendingdate,
Week(weeklytransactions.weekendingdate),
round(datediff(weekendingdate, (if(month(weekendingdate)>5,concat(year(weekendingdate),'-06-01'),concat(year(weekendingdate)-1,'-06-01'))))/7,0)+1 as fyweek,
cast((Case When Month(weeklytransactions.weekendingdate) >5 Then Concat(Year(weeklytransactions.weekendingdate), '-',Year(weeklytransactions.weekendingdate) + 1) Else Concat(Year(weeklytransactions.weekendingdate) - 1, '-',Year(weeklytransactions.weekendingdate)) End) as char) As fy,
business_units.business_unit
From departments Inner Join (business_units Inner Join weeklytransactions On business_units.buID = weeklytransactions.businessUnit) On departments.deptid = weeklytransactions.departmentId
Where (vat_rates.vat_from <= weeklytransactions.weekendingdate and business_units.Active = true and business_units.sales=1)
Group By weeklytransactions.weekendingdate, business_units.business_unit Order By fy desc, business_unit, fyweek
Regards
Pete
Assuming I read your question correctly, your problem is about having the result of another SELECT used to be returned by the result of your main query (plus depending on how acquainted you are with SQL, maybe you haven't had the occasion to learn about JOINs?).
You can have subqueries you extract data from within a SELECT, provided you define it within the FROMclause. The following query will work, for example:
SELECT A.a, B.b
FROM A
JOIN (SELECT aggregate(c) FROM C) AS B
Notice that there is no reference to table A within the subquery. Thing is, you cannot just add it like that to the query, as the subquery doesn't know it is a subquery. So the following won't work:
SELECT A.a, B.b
FROM A
JOIN (SELECT aggregate(c) FROM C WHERE C.someValue = A.someValue) AS B
Back to basics. What you want to do here visibly, is to aggregate some data associated to each of the records of another table. For that, you will need merge your SELECT queries and use GROUP BY:
SELECT A.a, aggregate(C.c)
FROM A, C
WHERE C.someValue = A.someValue
GROUP BY A.a
Back to your tables, the following should work:
SELECT w.weekendingdate, FORMAT(MAX(v.Vat_Rate, 3)
FROM weeklytransactions AS w, vat_rates AS v
WHERE v.Vat_From <= w.weekendingdate
GROUP BY w.weekendingdate
Feel free to add and remove fields and conditions as you see fit (I wouldn't be surprised that you'd also want to use a lower bound when filtering the records from vat_rates, since the way I have written it above, for a given weekendingdate, you get records from that week + the weeks before!).
So it looks like my first try did not address the actual problem. With the additional information provided in the comments, as well as the new complete query, let's see how this goes.
We are still missing error messages, but normally the query as written should result in MySQL having the following complaint:
ERROR 1109 (42S02): Unknown table 'vat_rates' in field list
Why? Because the vat_rates table does not appear in the FROM clause, whereas it should. Let's make that more obvious by simplifying the query, removing all references to the business_units table as well as the fields, calculations and order that do not add or remove anything to the problem, leaving us with the following:
SELECT MAX(vat_rates.vat_rate) AS r,
IF(d.vat, w.takings / r, w.takings) AS Total
FROM departments AS d
INNER JOIN weeklytransactions AS w ON w.departmentId = d.deptid
WHERE vat_rates.vat_from <= w.weekendingdate
GROUP BY w.weekendingdate
That cannot work, and will produce the error mentioned above. It looks like there is no FOREIGN ID between the weeklytransactions and vat_rates tables, so we have no choice but to do a CROSS JOIN for the moment, hoping that the condition in the WHERE clause and the aggregate function used to get r are enough to fit the business logic at hand here. The following query should return the expected data instead of an error message (I also remove r since that seems to be an intermediate value judging by the comments that were written):
SELECT IF(d.vat, w.takings / MAX(v.vat_rate), w.takings) AS Total
FROM vat_rates AS v, departments AS d
INNER JOIN weeklytransactions AS w ON w.departmentId = d.deptid
WHERE v.vat_from <= w.weekendingdate
GROUP BY w.weekendingdate
From there, assuming it works, you will only need to put back all the parts I removed to get your final query. I am a tad doubtful about the way the VAT rate is gotten here, but I have no idea what your requirements are in that regard so I leave it up to you to make sure that works as expected.

Correlated Subquery in a MySQL CASE Statement

Here is a brief explanation of what I'm trying to accomplish; my query follows below.
There are 4 tables and 1 view which are relevant for this particular query (sorry the names look messy, but they follow a strict convention that would make sense if you saw the full list):
Performances may have many Performers, and those associations are stored in PPerformer. Fans can have favorites, which are stored in Favorite_Performer. The _UpcomingPerformances view contains all the information needed to display a user-friendly list of upcoming performances.
My goal is to select all the data from _UpcomingPerformances, then include one additional column that specifies whether the given Performance has a Performer which the Fan added as their favorite. This involves selecting the list of Performers associated with the Performance, and also the list of Performers who are in Favorite_Performer for that Fan, and intersecting the two arrays to determine if anything is in common.
When I execute the below query, I get the error #1054 - Unknown column 'up.pID' in 'where clause'. I suspect it's somehow related to a misuse of Correlated Subqueries but as far as I can tell what I'm doing should work. It works when I replace up.pID (in the WHERE clause of t2) with a hard-coded number, and yes, pID is an existing column of _UpcomingPerformances.
Thanks for any help you can provide.
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT * FROM (
SELECT RID FROM Favorite_Performer
WHERE FanID = 107
) t1
INNER JOIN
(
SELECT r.ID as RID
FROM PPerformer pr
JOIN Performer r ON r.ID = pr.Performer_ID
WHERE pr.Performance_ID = up.pID
) t2
ON t1.RID = t2.RID
)
THEN "yes"
ELSE "no"
END as pText
FROM
_UpcomingPerformances up
The problem is scope related. The nested Selects make the up table invisible inside the internal select. Try this:
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT *
FROM Favorite_Performer fp
JOIN Performer r ON fp.RID = r.ID
JOIN PPerformer pr ON r.ID = pr.Performer_ID
WHERE fp.FanID = 107
AND pr.Performance_ID = up.pID
)
THEN 'yes'
ELSE 'no'
END as pText
FROM
_UpcomingPerformances up

MySQL SubQuery Conditional

I am trying to build a rather complex view in MySQL and want to do a conditional, but it sems always to fail.
My view (simplified) is
Select entry AS Entry, ,(select count(`poitems`.`entry`) AS `count(poitems.entry)` from `poitems` where (`poitems`.`PurchaseOrder` = `purchaseorder`.`entry`)) AS `TotalEntries`, from purchase orders
this is OK but what I am trying to do is add something like
if ((select count(`poitems`.`entry`) = 0),'query.png',NULL) AS Queryflag
or just test the value of TotalEntries.
Help appreciated! Thanks!
I'm not 100% sure on the names of the columns in purchaseorder or poitems tables but the following should get you headed in the right direction:
select t.Entry,
case when t.TotalEntries > 0 then 'query.png' else null end as Queryflag
from
(
select po.entry as Entry,
count(*) as TotalEntries
from purchaseorder po
left outer join poitems poi on poi.purchaseorder = po.entry
group by po.entry
) t;