Allowing Optional Parameters for MySQL Query - mysql

I have a search page that has multiple fields that are used to create a refined search. Every field is optional. I'm trying to start crafting my sql query so that it will work given the proper variables but I'm having trouble.
Here is the SQL query I currently have:
SELECT
indicator.indid,
indicator.indicator,
indtype.indtype,
provider.provider,
report.report,
actor.actor
FROM
actor,
indicator,
indtype,
report,
provider
WHERE
indicator.indtypeid = indtype.indtypeid
AND indicator.actorid = actor.actorid
AND indicator.reportid = report.reportid
AND report.providerid = provider.providerid
AND indicator.indicator LIKE '%$indicator%'
AND indicator.indtypeid = $indtypeid;
Whenever I provide an indicator and an indtypeid, the search works just fine. However, when I leave the indtypeid field blank, and have the variable set to * (as its default value), the query returns no results. I've tried playing with the query manually and it doesn't seem to like the * or a % sign. Basically, if only an indicator is specified and no indtypeid is specified, I want to return all indicators for all indtypeids.
I'm sure I'm missing something minor, but I would appreciate any assistance that could be provided. I may be going about this all wrong in the first place.

Try this instead:
SELECT i.indid, i.indicator, it.indtype,
p.provider, r.report, a.actor
FROM actor a
INNER JOIN indicator i ON a.actorid = i.actorid
INNER JOIN indtype it ON i.indtypeid = it.indtypeid
INNER JOIN report r ON i.reportid = r.reportid
INNER JOIN provider p ON r.providerid = p.providerid
WHERE 1 = 1
AND ($indicator IS NULL OR i.indicator LIKE '%$indicator%')
AND ($indtypeid IS NULL OR i.indtypeid = $indtypeid);
So if you pass a $indicator = NULL, then the first condition AND ($indicator IS NULL OR i.indicator LIKE '%$indicator%') will be ignored since it will resolve to True, and the same thing for the second condition.
I've removed other Where condition and replace them with JOINs, and for WHERE 1 = 1 to make the query work fine in case you pass the two variables $indicator and $indtypeid with NULL values for each, in this case it will return all results since 1 = 1 always true.

Related

Use ValueA when JOIN returns a row, otherwise ValueB (like a default)

I have four tables for a form-builder in my databse.
fields (fieldID(PK), typeID, fieldName, ...) - This table is a row by row list of all fields to be in the form
fields_types (typeID(PK), htmlType, ...) - This is a table that links fields to html types (and other settings)
fields_meta (FieldMetaID(PK), FieldID, mName, mValue) - Additional settings for fields, but more specific. A textarea field might have a height attribute, but almost no other field would use that.
fields_tyeps_meta (TypeMetaID(PK), typeID, tmName, tmValue) - Defines what extraneous settings a field can have, and also supplies default values if it's not explicitly set)
So my Query currently looks something like this
SELECT *
FROM Fields F
JOIN Field_Types FT
on FT.FieldID = F.FieldID
LEFT
JOIN Field_Meta FM
on FM.FieldID = F.FieldID
I was wondering if there's a way to join Fields_Types_Meta so that when the row's JOIN to Fields_Meta doesn't return a row (no mValue), it returns tmValue
I realize I can use something like (CASE WHEN mValue = "" THEN tmValue ELSE mValue END) AS UseValue, but I might have fields where I want to allow the value to be set to empty.
Edit: I could probably do something with a subquery and COUNT, using a CASE decision based on that. It might not be the healthiest performance-wise, but this query runs and caches itself til server restart, or until it's told to run again (updates to form design)
It looks like you just want ¢oalesce():
coalesce(FM.mValue, FT.tmValue) as UseValue
When FM.mValue is null, coalesce() returns FT.tmValue instead.
If you have null values in FM that you want to preserve in the result set, then use a case expression instead:
case when FM.FieldID IS NULL THEN FT.tmValue ELSE FM.mValue END as UseValue
This phrases as: when the left join did find a match in FM, use mValue from that row (even if it is null), else use FT.tmValue.

SQL: Something wrong with inheriting variables for NULL next-row values

I'm trying to inherit value from previous row (based on correct subscription_id + checking for IS NULL subscription_status), but something goes wrong and I get incorrect value.
Take a look at screenshot.
If I'm not mistaken it also called last non-null puzzle, but examples of possible solution for other DB provide window function with IGNORE NULLS.
But, I'm using MySQL 8.x and it doesn't support this function.
I'm sorry, but SQL fiddle doesn't provide correct text-value for variables in my code :(
https://www.db-fiddle.com/f/wHanqoSCHKJHus5u6BU4DB/4
Or, you can see mistakes here:
SET #history_subscription_status = NULL;
SET #history_subscription_id = 0;
SELECT
c.date,
c.user_id,
c.subscription_id,
sd.subscription_status,
(#history_subscription_id := c.subscription_id) as 'historical_sub_id',
(#history_subscription_status := CASE
WHEN #history_subscription_id = c.subscription_id AND sd.subscription_status IS NULL
THEN #history_subscription_status
ELSE
sd.subscription_status
END
) as 'historical'
FROM
calendar c
LEFT JOIN
subscription_data sd ON sd.date = c.date AND sd.user_id = c.user_id AND sd.subscription_id = c.subscription_id
ORDER BY
c.user_id,
c.subscription_id,
c.date
I expect to get results for this query in this way:
IMPORTANT: I'm going to use this code for a lot of data (about 1 mln rows), so it very important for me to avoid additional select or subquery that can slow down the execution of the query.

MySQL select unreaded messages

So i'm trying to select unread messages with a query, but the results are blank. I need to get the info from a seperate table read so it will know it was read or not. Im trying to do something like a IF statement so if readed not exists it will be unread, but i can't get it fixed
this is my query:
SELECT * FROM notify
INNER JOIN readed ON readed.acc_ID = '26' AND readed.user_ID = '6'
AND readed.msg_ID = notify.ID AND readed.readed != '1' OR readed.ID IS NULL
WHERE notify.groep = '1'
DB - readed
ID - int
user_ID - int
acc_ID - int
msg_ID - int
readed - enum ('0','1')
DB - notify
ID - int
notfi - text
thumb_src - text
title - text
url - text
groep - int
I hope someone know whats the problem!
The query seems correct to me, except the part with the OR, so I suppose that the problem is with the data. I will first try to show how you could improve the query nevertheless, and then try to show how to debug your data.
First, let's leave away the OR condition since this won't work as expected in a JOIN ON clause.
Second, when comparing integer fields to values, you should not put quotes around the values. This will only worry every person who tries to understand the query because the quotes denote string values, and it will worry (i.e. slow down) MySQL's parser, because it must convert the string values to numbers.
Third, mixing up normal WHERE conditions and JOIN ON conditions is worrying and bad style (IMHO). I always recommend to put only the conditions which actually link the tables into the JOIN ON clause, and other conditions elsewhere.
Following this advice would lead to something like that:
SELECT * FROM
notify INNER JOIN readed ON
readed.msg_ID = notify.ID
WHERE
readed.acc_ID = 26 AND
readed.user_ID = 6 AND
readed.readed != '1' AND
notify.groep = 1
This should do the same as your original query minus the OR part.
Now, since we suspect that there is a problem with the data, we can begin to debug the data. First, leave away the WHERE clause:
SELECT * FROM
notify INNER JOIN readed ON
readed.msg_ID = notify.ID
If this returns data, then you at least know that there are rows in the readed table matching rows in the notify table. If it does not return any data, then there are no rows which fit together, and have found the root of your problem.
Provided that the above returns data, re-add the WHERE clause line by line and test after each step. For example, start with
SELECT * FROM
notify INNER JOIN readed ON
readed.msg_ID = notify.ID
WHERE
readed.acc_ID = 26 AND
and continue with
SELECT * FROM
notify INNER JOIN readed ON
readed.msg_ID = notify.ID
WHERE
readed.acc_ID = 26 AND
readed.user_ID = 6 AND
and so on, testing the query each time.
That way, you hopefully will find out where the problem is. I am convinced that the problem is with the data, not with the query.

SQL Joining Diffrent Size Tables Together With Null Value Replacement

I am working on a query for a datatable and I can't seem to get it to display how I want, I don't know if this is even possible in SQL What I am looking to do is get a query to respond with ideally an extra column of Boolean type.
Currently I can run two queries and they both work perfectly but I can't work out how to join them together bellow is the code from my first query what this does is return beers a user has tried this works fine and as expected and returns as expected.
SELECT *
FROM keg.beer
JOIN keg.userbeer
ON beer.id = userbeer.beer_id
WHERE userbeer.username_id = 1;
The second query is even simpler and is just a select getting the list of beers.
SELECT * FROM keg.beer
What I want to do is run a query and have it return a list of beers with a Boolean value if the user has tried it or not.
You're not going to run into too many scenarios for "Desired Results" that can't be produced with plain 'ol SQL. In this case you'll use a CASE statement to determine if the person has tried a beer. You'll also want a LEFT OUTER JOIN so you don't drop records coming from your beer table when your filtered userid doesn't have a userbeer record for that beer:
SELECT
beer.name,
beer.id,
beer.country,
CASE WHEN userbeer.username_id IS NULL THEN 0 ELSE 1 END AS user_tried_beer_boolean
FROM keg.beer
LEFT OUTER JOIN keg.userbeer
ON beer.id = userbeer.beer_id
AND userbeer.username_id = 1;
As #SeanLange mentioned in the comments here, the restriction of the WHERE statement for the userid would cause records to be dropped that you want in your result set, so we move the restriction of username_id = 1 to the ON portion of the LEFT OUTER JOIN so that the userbeer table results are restricted to just that user before it's joined to the beer table.
Now I need a drink.
SELECT b.id,
b.name,
CASE WHEN u.username_id IS NOT NULL THEN TRUE ELSE FALSE END AS userdrankbeer
FROM keg.beer b
LEFT JOIN ( SELECT * FROM keg.userbeer WHERE username_id = 1 ) u
ON beer.id = userbeer.beer_id
;

Having trouble with an IFNULL in a mySQL WHERE clause

Before anyone says, I have searched through for a suitable answer for my issue but cannot find anything specific enough so I thought I'd ask it.
Basically I am trying to select a bunch of data for a report of people who have made loan applications to a website, but there are two different types: unsecured and guarantee. I need to place an IFNULL statement in the WHERE clause so that I ONLY use that clause if a certain other field isn't null.
Here is my statement:
SELECT
la.`lms_loan_application_id`,
la.`created`,
la.`updated`,
la.`loan_amount`,
la.`loan_term`,
la.`loan_document_fee`,
la.`broker_reference`,
la.`broker_sub_reference`,
laa.`first_name`,
laa.`surname`,
laa.`dob`,
laa.`email`,
laa.`mobile_number`,
laaAd.`address_postcode`,
lag.`first_name`,
lag.`surname`,
lag.`dob`,
lag.`email`,
lag.`mobile_number`,
lagAd.`address_postcode`,
lagAd.`housing_status`
FROM
loan_application AS la
JOIN
loan_application_applicant AS laa ON la.`id` = laa.`loan_application`
LEFT JOIN
loan_application_guarantor AS lag ON la.`id` = lag.`loan_application`
JOIN
loan_application_address AS laaAd ON laaAd.`loan_application_applicant` = laa.`id`
LEFT JOIN
loan_application_address AS lagAd ON lagAd.`loan_application_guarantor` = lag.`id`
WHERE
la.`status` = 'signature_given'
AND ! IFNULL(lag.`first_name`,
lag.`status` = 'signature_given')
AND laa.`status` = 'signature_given'
AND ! IFNULL(lag.`first_name`,
lagAd.`current_address` = 1)
AND laaAd.`current_address` = 1
ORDER BY la.`updated` DESC
LIMIT 10000
As you can see, I have attempted to use the IFNULLs (although in a negated way, which I assume works?) but all I get is duplicate row results and not the result set I really want.
Basically, I need to use the where clause "lag.status = 'signature_given" and "lagAd.current_address = 1" ONLY if the lag.first_name field is NOT null (i.e. there is a guarantor name) otherwise the status won't exist, and therefore the results of unsecured loans will not show. Hope I'm explaining this well enough!
In summary, I need to show all loan information, unsecured and guaranteed, and use a negated IFNULL in order to determine when the WHERE clause is to be taken into consideration.
Any help appreciated!
Thank you in advance
Michael
From this MySQLTutorial article:
Notice that you should avoid using the IFNULL function in the WHERE clause, because it degrades the performance of the query. If you want to check if a value is NULL or not, you can use IS NULL or IS NOT NULL in the WHERE clause.
Here is a WHERE clause which implements your logic correctly using IS NULL and IS NOT NULL instead of IFNULL:
WHERE la.`status` = 'signature_given' AND
(lag.`first_name` IS NULL OR
(lag.`first_name` IS NOT NULL AND lag.`status` = 'signature_given')) AND
laa.`status` = 'signature_given' AND
(lag.`first_name` IS NULL OR
(lag.`first_name` IS NOT NULL AND lagAd.`current_address` = 1)) AND
laaAd.`current_address` = 1