mySQL: LEFT JOIN where joining needs to be done on different type of data - mysql

I have 2 my tables with data and 2 "not mine" tables (in ReferenceDB) where thing ID can be mapped to its name.
One of mine tables is orders with following important columns: charName, stationID, typeID, bid.
Another table has following important columns: transactionDateTime, stationID, typeID, person, transactionType
I started my head braking with idea how to find orders that doesn't have any records for them lately (e.g. given amount of days). But for beginning I set me a task just to find orders that has no records for them at all. For that I figured out LEFT JOIN see biggest query below.
An order for me is a combination of charName/persone + stationID + typeID + transactionType/bid so if actually one of those four changes it is different order then.
Problem is that transactionType can be "yes" or "no" and bid is 0 or not 0. So I cant or DON'T KNOW HOW to JOIN ON different data types. So logically I'd like to join on 4 columns like:
FROM ordersTable LEFT JOIN recordsTable ON ordersTable.typeID = recordsTable.typeID
AND ordersTable.stationID = recordsTable.stationID
AND ordersTable.charName = recordsTable.person
AND ordersTable.bid = recordsTable.transactionType
Clearly last string of above wouldn't work cause of different data types.
So for a moment I thought that I can do such query twice for bid=0 with transactionType="yes" and second time for bid != 0 and transactionType = "no" see my query below for 0/"yes" combination. But seems it doesn't works exactly as I'd like it to. because AND ordersTable.bid IN (0) AND recordsTable.transactionType="yes" in JOIN ON doesn't sem do anything. (As I do get results where bid=1)
SELECT invTypes.typeName, stastations.stationName, main.* FROM referenceDB.invTypes, referenceDB.stastations, (
SELECT ordersTable.charName, ordersTable.stationID, ordersTable.typeID, ordersTable.bid, ordersTable.orderState, ordersTable.volRemaining
FROM ordersTable LEFT JOIN recordsTable ON ordersTable.typeID = recordsTable.typeID
AND ordersTable.stationID = recordsTable.stationID
AND ordersTable.charName = recordsTable.person
AND ordersTable.bid IN (0) AND recordsTable.transactionType="yes"
WHERE recordsTable.typeID IS NULL
AND ordersTable.orderState IN (0) ) as main
WHERE stastations.stationID = main.stationID AND invTypes.typeID = main.typeID;
Questions:
Is it possible to tell mySQL to treat "yes" as 0 or vise versa? If yes how do I do it in my query? If no what would be my work around (to find orders that doesn't have records related to them)?
And possibly some one can suggset a query that will find orders that didn't have records within given amount of days?
Thank you in advance!

One way is to use the explicit comparisons:
((ordersTable.bid = 0 and recordsTable.transactionType = 'No') or
(ordersTable.bid = 1 and recordsTable.transactionType = 'Yes')
)
Another would be to use a case statement:
(case when recordsTable.transactionType = 'No' then 0 else 1 end) = ordersTable.bid

SELECT invTypes.typeName, stastations.stationName, main.* FROM referenceDB.invTypes, referenceDB.stastations, (
SELECT ordersTable.charName, ordersTable.stationID, ordersTable.typeID, ordersTable.bid, ordersTable.orderState, ordersTable.volRemaining
FROM ordersTable LEFT JOIN recordsTable ON ordersTable.typeID = recordsTable.typeID
AND ordersTable.stationID = recordsTable.stationID
AND ordersTable.charName = recordsTable.person
AND ((ordersTable.bid = 0 AND recordsTable.transactionType = 'yes') OR
(ordersTable.bid != 0 AND recordsTable.transactionType = 'no'))
WHERE recordsTable.typeID IS NULL
AND ordersTable.orderState IN (0) ) as main
WHERE stastations.stationID = main.stationID AND invTypes.typeID = main.typeID;

Related

mysql select counts by if else condition

i have a table named cq500_all(to record diffrent doctor feedback)
now i want know counts when condition status is
field dr_1_finish and field dr_2_finish value is all fill 1
and
when field dr_1 different dr_2 (like dr_1=1 and dr_2=0,or dr_1=0 and dr_2=1 )
cause i want to know two doctors feedback counts (when different doctor's feedback on jpg)
for example image show CQ500-CT-1_36_08.jpg and CQ500-CT-1_36_09.jpg is match my select counts
it will be two (select counts result)
how to make the query on mysql?
You can count as
select count(*) as total
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1 and dr_1 != dr_2
You will got result in total
Pretty much just the way you've described it:
select *
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1
and dr_1 != dr_2
or (if dr_1 or dr_2 might not be just 0 and 1):
select *
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1
and ((dr_1 = 1 and dr_2 = 0) or (dr_1 = 0 and dr_2 = 1))

How to select sql with if else use column

I'm beginner for mysql and this my graduation project please help me.
//AMOUNT_PEOPLE is variable in nodejs
//place_id is variable in nodejs recieve from front end.
SELECT
(IF AMOUNT_PEOPLE >= 10
RETURN COLUMN package_tb.price_group*(AMOUNT_PEOPLE-1)
ELSE IF AMOUNT_PEOPLE >= 6
RETURN COLUMN package_tb.price_group*(AMOUNT_PEOPLE-1) - (SELECT option_tb.price_group FROM option_tb WHERE obtion_tb.place_id = place_id)
ElSE
RETURN COLUMN price_normal*AMOUNT_PEOPLE
END IF) AS price,name,detail
FROM package_tb WHERE package_tb.place_id = place_id
This ticket booking program
Logic is
Check number of tourist
if tourist >= 10 must use group_price and free 1 person include food for free person option
but if tourist >= 6 must use group_price
and free 1 person but not include food for free person option
finally tourist 0-5 must use normal_price
Such customer tell me "I want ticket for 10 tourist" the system will check as above explain.
package_tb
-package_id
-place_id
-name
-detail
-price_group
-price_normal
option_tb
-option_id
-place_id
-name
-price_group
-price_normal
place_tb
-place_id
-name
If tourist use price group option have to use price group only
But tourist use price normal option have to use price normal only.
Sorry for my bad english.
There exists a CASE function.
Which is a standard SQL function for switch logic.
Based on the updated question I'm assuming that there can be multiple options per place.
So with node.js variables:
SET #place_id = ${PLACE_ID};
SET #amount_people = ${AMOUNT_PEOPLE};
SELECT
CASE
WHEN #amount_people >= 10
THEN (p.price_group * (#amount_people - 1))
WHEN #amount_people >= 6
THEN (p.price_group * (#amount_people - 1)) - SUM(o.price_group)
ELSE p.price_normal
END AS price,
p.name,
p.detail
FROM package_tb p
LEFT JOIN option_tb o ON o.place_id = p.place_id
WHERE p.place_id = #place_id
GROUP BY p.package_id, p.place_id, p.price_normal, p.price_group, p.name, p.detail;
A test on rextester here
-- From which table you want use price_normal? tb or o ???
SELECT
CASE WHEN p.AMOUNT_PEOPLE >= 4
THEN p.price_group
WHEN p.AMOUNT_PEOPLE >= 3
THEN p.price_group - o.price_food
ELSE tb.price_normal
END AS price,
p.name, p.detail
FROM package_tb p JOIN option_tb o ON o.package_id = p.package_id;
There are two main control-flow functions in MySQL IF() and CASE
Since you are not comparing the value of AMOUNT_PEOPLE directly, CASE is a bit of overkill, which can be simplified slightly by using IF.
The syntax for IF is IF(<expr>, <true_result>, <false_result>). This allows you to perform else if by chaining another IF() as the false_result
IF(<expr>, ..., IF(<expr>, ..., ...))
Instead of using else if, you only need to remove the option_tb.price_group when AMOUNT_PEOPLE is fewer than 10 to get your desired pricing.
/* groups with 6 or more use group price and one person free */
IF(AMOUNT_PEOPLE >= 6,
/* groups with fewer than 10 people remove option */
p.price_group*(AMOUNT_PEOPLE-1) - IF(AMOUNT_PEOPLE < 10,
o.price_group,
0
),
p.price_normal*AMOUNT_PEOPLE
) AS price
This reduces the amount of code slightly, to determine when to subtract a person.
Instead of using a nested sub-query, which would be executed for each row returned. If the option_tb.place_id is unique, a JOIN would be more preferable.
If option_tb.place_id is not unique, you would need to use a GROUP BY. One approach is to JOIN using a sub-query, to avoid false matching on the join table groupings.
To ensure results are not excluded when a row in the option_tb table fails to match a place_id, you would use a LEFT JOIN that returns NULL instead of excluding the row.
Then you can use COALESE(<column>, 0) to retrieve the value from the column or 0 if the column value is NULL.
In NodeJS you can use ${var} to inject a variable into a string.
For example:
var place_id = 1;
var query = 'SELECT ${place_id};';
console.log(query);
Results in
SELECT 1;
Putting it all together.
Example: db-fiddle
SELECT
IF(${AMOUNT_PEOPLE} >= 6,
p.price_group*(${AMOUNT_PEOPLE}-1) - IF(${AMOUNT_PEOPLE} < 10, COALESCE(o.price_group, 0), 0),
p.price_normal*${AMOUNT_PEOPLE}
) AS price,
p.name,
p.detail
FROM package_tb AS p
LEFT JOIN (
SELECT
place_id,
SUM(price_group) AS price_group
FROM option_tb
GROUP BY place_id
) AS o
ON o.place_id = p.place_id
WHERE p.place_id = ${place_id};

My SUM with cases seems to be repeating twice

I have some camp management software that registers users for a camp.
I am trying to get how much a user owes on their account based on how much a camp costs and whether they are using the bus, and whether or not they sign up for the horse option. (These all cost extra).
I originally was grouping by registration_ids which a camper can have multiple of if they sign up for a camp. But when I put this in I get this:
https://imgur.com/i63Bnsu
This is my sql:
SELECT srbc_campers.camper_id,
/*Calculate how much the user owes*/
SUM(
srbc_camps.cost + (CASE WHEN srbc_registration.horse_opt = 1 THEN srbc_camps.horse_opt_cost
ELSE 0
END)
+
(CASE WHEN srbc_registration.busride = 'to' THEN 35
WHEN srbc_registration.busride = 'from' THEN 35
WHEN srbc_registration.busride = 'both' THEN 60
ELSE 0
END)
- IF(srbc_registration.discount IS NULL,0,srbc_registration.discount)
- IF(srbc_registration.scholarship_amt IS NULL,0,srbc_registration.scholarship_amt)
) AS owe
FROM (
srbc_registration INNER JOIN srbc_camps ON srbc_registration.camp_id=srbc_camps.camp_id)
INNER JOIN srbc_payments ON srbc_registration.registration_id = srbc_payments.registration_id)
INNER JOIN srbc_campers ON srbc_campers.camper_id=srbc_registration.camper_id)
WHERE NOT srbc_payments.payment_type='Store'
GROUP BY srbc_campers.camper_id
This seems to be affected by how many payments they have made in their account. It multiplies the amount they owe times how many individual payments were made toward that camp. I can't figure out how to stop this.
For instance in picture above^
We have camper_id #4 and they owe 678.
I expect camper_id #4 to owe 339. They have made 2 payments on their account in srbc_payments.
Haven't been using sql for that long, so any suggestions for a better way I am open too!
You are not selecting anything from srbc_payments, just checking for registration_id in srbc_payments. Or did you forget to subtract payments from srbc_payments? You can replace the inner join with:
where srbc_registration.registration_id in
(
select t1.registration_id from srbc_payments t1
where t1.registration_id = srbc_registration.registration_id
and t1.payment_type <> 'Store'
)
This is what I ended up getting to work how I wanted it too:
SELECT owedTble.registration_id,owe
FROM (SELECT registration_id,
SUM(
srbc_camps.cost + (CASE WHEN srbc_registration.horse_opt = 1 THEN srbc_camps.horse_opt_cost
ELSE 0
END)
+
(CASE WHEN srbc_registration.busride = 'to' THEN 35
WHEN srbc_registration.busride = 'from' THEN 35
WHEN srbc_registration.busride = 'both' THEN 60
ELSE 0
END)
- IF(srbc_registration.discount IS NULL,0,srbc_registration.discount)
- IF(srbc_registration.scholarship_amt IS NULL,0,srbc_registration.scholarship_amt)
) AS owe
FROM srbc_camps INNER JOIN srbc_registration ON srbc_camps.camp_id=srbc_registration.camp_id
GROUP BY srbc_registration.registration_id
) as owedTble
I kind of understand what I did here. I ended up trying different things from this answer: My SUM with cases seems to be repeating twice
Thanks for the helpful comments from #nick and #a_horse_with_no_name

How to replace one value calculated by COUNT with another value in SELECT result?

Suppose there are two tables
shop seller
------ -------
id id
seller_id seller_name
goods sallary
seller_id may be repeated, parts from seller.id may not be there at all. So, I need to get the result of the request consisting of saller_name and counting the number of stores on each seller. I did it like this
SELECT seller_name, COUNT(seller_id) AS shops FROM seller
LEFT JOIN shop ON seller.id = shop.seller_id GROUP BY seller_name.
And it works. But the problem is that I need to replace all 0 in shops with the text 'none'.
I tried to do it through
REPLACE(COUNT(seller_id),0,"none")
and with
CASE
WHEN COUNT(seller_id)=0 THEN "none"
ELSE COUNT(seller_id)
END
But instead of the expected numbers and none, I get a random set of numbers and letters. How to do it?
The problem is that 'none' is a string, but the value is a count. I would strongly advise you to stick with numbers, but that is not your query.
You need to do type conversion:
SELECT se.seller_name,
(CASE WHEN COUNT(s.seller_id) = 0
THEN 'none'
ELSE CAST(COUNT(s.seller_id) AS VARCHAR(255))
END) AS shops
FROM seller se LEFT JOIN
shop s
ON se.id = sh.seller_id
GROUP BY se.seller_name;
In MySQL, use CAST(COUNT(s.seller_id) AS CHAR). Or almost any database supports CONCAT(COUNT(s.seller_id), ''), but using CONCAT() to convert types seems misleading to me.
Point 1: REPLACE(COUNT(seller_id),0,"none") --It will replace all zero's with 'none'.
Like your count is 10 it will return 1none in output.
Point 2: CASE WHEN COUNT(seller_id)=0 THEN "none" ELSE COUNT(seller_id) END--It will gives you conversion failed error.
So you need you cast the count in varchar as given below.
SELECT se.seller_name,
(CASE WHEN COUNT(s.seller_id) = 0
THEN 'none'
ELSE CAST(COUNT(s.seller_id) AS VARCHAR(10))
END) AS shops
FROM seller se LEFT JOIN
shop s
ON se.id = sh.seller_id
GROUP BY se.seller_name;
You need to change yor query to something like this:
SELECT seller_name,
CASE
WHEN COUNT(shop.Id)=0 THEN 'none'
ELSE CAST(COUNT(shop.Id) as NVARCHAR(10))
END AS shops FROM seller
LEFT JOIN shop ON seller.id = shop.seller_id GROUP BY seller_name

filed showing null value when joining table

below is my query
select C.cName,DATE_FORMAT(CT.dTransDate,'%d-%M-%Y') as dTransDate,
(c.nOpBalance+IFNULL(CT.nAmount,0)) AS DrAMount,IFNULL(CTR.nAmount,0) AS
CrAMount,((c.nOpBalance+IFNULL(CT.nAmount,0))-IFNULL(CTR.nAmount,0)) AS
Balance,CT.cTransRefType,CT.cRemarks,cinfo.cCompanyName,cinfo.caddress1,cinfo.cP
honeOffice,cinfo.cMobileNo,cinfo.cEmailID,cinfo.cWebsite from Customer
C LEFT JOIN Client_Transaction CT ON CT.nClientPk = C.nCustomerPk AND
CT.cTransRefType='PAYMENT' AND CT.cClientType='CUSTOMER' AND CT.dTransDate
between '' AND '' LEFT JOIN Client_Transaction CTR ON CTR.nClientPk =
C.nCustomerPk AND CTR.cTransRefType='RECEIPT' AND
CTR.cClientType='CUSTOMER' AND CTR.dTransDate between '2015-05-01' AND
'2015-05-29' LEFT JOIN companyinfo cinfo ON cinfo.cCompanyName like
'%Fal%' Where C.nCustomerPk = 4 Order By dTransDate
it's showing all value but dTransDate ,cTransRefType,cRemarks, showing null.
One obvious thing jumps out at us:
CT.dTransDate BETWEEN '' AND ''
^^ ^^
Another thing that jumps out at us is that there's a semi-Cartesian join between rows from CT and rows from CTR. If 5 rows are returned from CT for a given customer, and 5 rows are returned from CTR, that's going to produce a total of 5*5 = 25 rows. That just doesn't seem like a resultset that you'd really want returned.
Also, if more than one row is returned from cinfo, that's also going to cause another semi-Cartesian join. If there's two rows returned from cinfo, the total number or rows in the resultset will be doubled. It's valid to do that in SQL, but this is an unusual pattern.
The calculation of the balance is also very strange. For each row, the nAmount is added/subtracted from opening balance. On the next row, the same thing, on the original opening balance. There's nothing invalid SQL-wise with doing that, but the result being returned just seems bizarre. (It seems much more likely that you'd want to show a running balance, with each transaction.)
Another thing that jumps out at us is that you are ordering the rows by a string representation of a DATE, with the day as the leading portion. (As long as all the rows have date values in the same year and month, that will probably work, but it just seems bizarre that we wouldn't sort on the DATE value, or a canonical string representation.
I strongly suspect that you want to run a query that's more like this. (This doesn't do a "running balance" calculation. It does return the 'PAYMENT' and 'RECEIPT' rows as individual rows, without producing a semi-Cartesian result.
SELECT c.cName
, DATE_FORMAT(t.dTransDate,'%d-%M-%Y') AS dTransDate
, C.nOpBalance
, IF(t.cTransRefType='PAYMENT',IFNULL(t.nAmount,0),0) AS DrAMount
, IF(t.cTransRefType='RECEIPT',IFNULL(t.nAmount,0),0) AS CrAMount
, t.cTransRefType
, t.cRemarks
, ci.*
FROM Customer c
LEFT
JOIN Client_Transaction t
ON t.nClientPk = c.nCustomerPk
AND t.cClientType = 'CUSTOMER'
AND t.dTransDate >= '2015-05-01'
AND t.dTransDate <= '2015-05-29'
AND t.cTransRefType IN ('PAYMENT','RECEIPT')
CROSS
JOIN ( SELECT cinfo.cCompanyName
, cinfo.caddress1
, cinfo.cPhoneOffice
, cinfo.cMobileNo
, cinfo.cEmailID
, cinfo.cWebsite
FROM companyinfo cinfo
WHERE cinfo.cCompanyName LIKE '%Fal%'
ORDER BY cinfo.cCompanyName
LIMIT 1
) ci
WHERE c.nCustomerPk = 4
ORDER BY t.dTransDate, t.cTransRefTpye, t.id