I've tried looking it up, and while I think this should be possible I can't seem to find the answer I need anywhere.
I need to lookup a date from one table, then store it for use in a following query.
Below is statements that should work, with my setting the variable (which I know won't work, but I'm unsure the best way to do/show it otherwise - bar maybe querying it twice inside the if statement.)
I'm then wanting to in the latter statement, use either the date given in the second query, or if the date from the first query (that I'm thinking to set as a variable) is newer, use that instead.
startDateVariable = (SELECT `userID`, `startDate`
FROM `empDetails`
WHERE `userID` = 1);
SELECT `userID`, SUM(`weeksGROSS`) AS yearGROSS
FROM `PAYSLIP`
WHERE `date` <= "2021-11-15"
AND `date` >= IF( "2020-11-15" > startDateVariable , "2020-11-15" , startDateVariable )
AND `userID` IN ( 1 )
GROUP BY `userID`
Naturally all dates given in the query ("2021-11-15" etc) would be inserted dynamically in the prepared statement.
Now while I've set the userID IN to just query 1, it'd be ideal if I can lookup multiple users this way at once, though I can accept that I may need to make an individual query per user doing it this way.
Much appreciated!
So turns I was going about this the wrong way, looks like the best way to do this or something similar is by using SQL JOIN
This allows you to query the tables as if they are one.
I also realised rather then using an IF, i could simply make sure i was looking up newer or equal to both the date given and the start date.
Below is working as required. And allows lookup of multiple users at once as wanted.
SELECT PAYSLIP.userID, employeeDetails.startDate, SUM(PAYSLIP.weeksGROSS) AS yearGROSS
FROM PAYSLIP
INNER JOIN employeeDetails ON employeeDetails.userID=PAYSLIP.userID
WHERE PAYSLIP.date <= "2021-11-15"
AND PAYSLIP.date >= "2020-11-15"
AND PAYSLIP.date >= employeeDetails.startDate
AND PAYSLIP.userID IN ( 1,2,8 )
GROUP BY PAYSLIP.userID
See here for more usage examples: https://www.w3schools.com/sql/sql_join.asp
However along the lines of my particular question, it's possible to store variables. I.E.
SET #myvar= 'Example showing how to declare variable';
Then use it in the SQL statement by using
#myvar where you want the variable to go.
Related
How to select COALESCE result to format( , 0)
my query is
SELECT (COALESCE((SELECT SUM(`invoices`.`paid_amount`) FROM `invoices`
WHERE DATE(`invoices`.`date`)=CURDATE()),0) +
COALESCE((SELECT SUM(`other_incomes`.`other_income_amount`) FROM `other_incomes`
WHERE DATE(`other_incomes`.`date`)=CURDATE()),0))
AS total
FROM
....
Primarily, COALESCE doesn't change the formatting. It only returns the first non-null value passed to it.
Also, instead of trying to join or do two different queries and adding, and handling all the sums and coalesces separately (not to mention the rounding), I would probably UNION all the relevant results together, then handle the coalesce/sum/round all at the end.
Try this:
SELECT round(sum(coalesce(amt, 0)), 0) as total
FROM (
SELECT paid_amount as amt
FROM invoices i
WHERE date(i.date) = CURDATE()
union all
SELECT other_income_amount
FROM other_incomes o
WHERE date(o.date) = CURDATE()
) z
Here I COALESCE first, to make nulls be 0 instead. I wrap that in a SUM to add up the values, and finally a ROUND to get the format. It was unclear from the question is you wanted to ROUND or FLOOR. If you are looking to get it with that comma, use FORMAT. Here's the mySQL documentation for that. You didn't specify your SQL flavor.
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_format
Additionally, you should include your sql platform and version, the create statements for your tables, along with some insert statements that will provide sample data, along with the results you are looking for. It will help people answer your question. If you can include a fiddle, like https://dbfiddle.uk/, that would be nice.
I am looking for a way to use a parameter as a result that can be plugged in to another select statement later down the line.
This is the CTE select statement that I am able to pull by manually changing all four of the dates listed to what I want
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> '2016-11-20'
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = '2016-11-20'
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
I am able to replace the first half of the WHEN statement with a parameter like this
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> #PREndDate
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = #PREndDate
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
But it will only produce the results I am looking for if the parameter is 2016-11-20. I want to be able to have #PREndDate as '2017-02-14' it will post 2017-02-14 as a result of the select statement.
You can use a Table Valued Function as HABO has already said. Here is a simple example for one:
/*
--Create some dummy data in a db
CREATE TABLE SomeData
(
DataId INT IDENTITY
, Val VARCHAR(8)
, Dt DATE
)
INSERT INTO dbo.SomeData (Val, Dt) VALUES ('A', '2017-1-1'),('B', '2017-1-2'),('C', '2017-1-3'),('D', '2017-1-4'),('E', '2017-1-5')
--Create a table valued function
CREATE FUNCTION ReturnData (#StartDt DATE, #EndDt Date)
RETURNS TABLE
AS
Return
Select *
From SomeData
WHERE DT between #StartDt and #EndDt
*/
Select *
From ReturnData('1-1-2017', '1-3-2017')
The best thing IMHO about Table Functions is they can join to existing things as they are well formed objects in the database. When you do Procedures and Dynamic SQL, you get a result set but that does not mean your code can be joined. Table Functions can be cross applied to run off of cte's and they can be joined to other tables. So essentially they are better for reuse but they have rules around them so you cannot do more advanced things like dynamic sql in them(as far as I know unless they changed it recently) and other things. But if you want a reusable data set with rules governing input, that is pretty much exactly what they are made for.
This case is similar to: S.O Question; mySQL returns all rows when field=0, and the Accepted answer was a very simple trick, to souround the ZERO with single quotes
FROM:
SELECT * FROM table WHERE email=0
TO:
SELECT * FROM table WHERE email='0'
However, my case is slightly different in that my Query is something like:
SELECT * FROM table WHERE email=(
SELECT my_column_value FROM myTable WHERE my_column_value=0 AND user_id =15 LIMIT 1 )
Which in a sense, becomes like simply saying: SELECT * FROM table WHERE email=0, but now with a Second Query.
PLEASE NOTE: It is a MUST that I use the SECOND QUERY.
When I tried: SELECT * FROM table WHERE email='( SELECT my_column_value FROM myTable WHERE my_column_value=0 LIMIT 1 )' (Notice the Single Quotes on the second query)
MySql SCREAMED Errors near '(.
How can this be achieved
Any Suggestion is highly honored
EDIT1: For a visual perspective of the Query
See the STEN_TB here: http://snag.gy/Rq8dq.jpg
Now, the main aim is to get the sten_h where rawscore_h = 0;
The CURRENT QUERY as a whole.
SELECT sten_h
FROM sten_tb
WHERE rawscore_h = (
SELECT `for_print_stens_rowscore`
FROM `for_print_stens_tb`
WHERE `for_print_stens_student_id` =3
AND `for_print_stens_factor_name` = 'Factor H' )
The result of the Second Query can be any number including ZERO.
Any number from >=1 Works and returns a single corresponding value from sten_h. Only =0 does not Work, it returns all rows
That's the issue.
CORRECT ANSWER OR SOLUTION FOR THIS
Just in case someone ends up in this paradox, the Accepted answer has it all.
SEE STEN_TB: http://snag.gy/Rq8dq.jpg
SEE The desired Query result here: http://snag.gy/wa4yA.jpg
I believe your issue is with implicit datatype conversions. You can make those datatype conversions explicit, to gain control.
(The "trick" with wrapping a literal 0 in single quotes, that makes the literal a string literal, rather than a numeric.)
In the more general case, you can use a CAST or CONVERT function to explicitly specify a datatype conversion. You can use an expression in place of a column name, wherever you need to...
For example, to get the value returned by my_column_value to match the datatype of the email column, assuming email is character type, something like:
... email = (SELECT CONVERT(my_column_value,CHAR(255)) FROM myTable WHERE ...
or, to get the a literal integer value to be a string value:
... FROM myTable WHERE my_column_value = CONVERT(0,CHAR(30)) ...
If email and my_column_value are just indicating true or false then they should almost certainly be both BIT NOT NULL or other two-value type that your schema uses for booleans. (Your ORM may use a particular one.) Casting is frequently a hack made necessary by a poor design.
If it should be a particular user then you shouldn't use LIMIT because tables are unordered and that doesn't return a particular user. Explain in your question what your query is supposed to return including exactly what you mean by "15th".
(Having all those similar columns is bad design: rawscore_a, sten_a, rawscore_b, sten_b,... . Use a table with two columns: rawscore, sten.)
i'm preparing a presentation about one of our apps and was asking myself the following question: "based on the data stored in our database, how much growth have happend over the last couple of years?"
so i'd like to basically show in one output/graph, how much data we're storing since beginning of the project.
my current query looks like this:
SELECT DATE_FORMAT(created,'%y-%m') AS label, COUNT(id) FROM table GROUP BY label ORDER BY label;
the example output would be:
11-03: 5
11-04: 200
11-05: 300
unfortunately, this query is missing the accumulation. i would like to receive the following result:
11-03: 5
11-04: 205 (200 + 5)
11-05: 505 (200 + 5 + 300)
is there any way to solve this problem in mysql without the need of having to call the query in a php-loop?
Yes, there's a way to do that. One approach uses MySQL user-defined variables (and behavior that is not guaranteed)
SELECT s.label
, s.cnt
, #tot := #tot + s.cnt AS running_subtotal
FROM ( SELECT DATE_FORMAT(t.created,'%y-%m') AS `label`
, COUNT(t.id) AS cnt
FROM articles t
GROUP BY `label`
ORDER BY `label`
) s
CROSS
JOIN ( SELECT #tot := 0 ) i
Let's unpack that a bit.
The inline view aliased as s returns the same resultset as your original query.
The inline view aliased as i returns a single row. We don't really care what it returns (except that we need it to return exactly one row because of the JOIN operation); what we care about is the side effect, a value of zero gets assigned to the #tot user variable.
Since MySQL materializes the inline view as a derived table, before the outer query runs, that variable gets initialized before the outer query runs.
For each row processed by the outer query, the value of cnt is added to #tot.
The return of s.cnt in the SELECT list is entirely optional, it's just there as a demonstration.
N.B. The MySQL reference manual specifically states that this behavior of user-defined variables is not guaranteed.
I've written SELECT statement that works perfectly. However, I need to make some changes so that it now gets user submitted information from a form and returns results from the database based on that.
It will essentially be a product search—if a user searches for "red" they'll get back all items that are red. If they search for "red" and "wood" they'll get back only the items that are red and made of wood.
Here is my HAVING clause:
HAVING values LIKE "%Red%" AND values LIKE "%Wood%"
If I had a series of 5 drop down menus, each with a different set of terms, should I try to build the HAVING clause dynamically based on what drop downs the user uses? How would this be done?
I would opt for a static SQL statement, given that there are exactly five "options" the user can select from.
What I would do is use an empty string as the value that represents "no restriction" for a particular option, so my statement would be like this: e.g.
HAVING `values` LIKE CONCAT('%',:b1,'%')
AND `values` LIKE CONCAT('%',:b2,'%')
AND `values` LIKE CONCAT('%',:b3,'%')
AND `values` LIKE CONCAT('%',:b4,'%')
AND `values` LIKE CONCAT('%',:b5,'%')
To apply restrictions only on options 1 and 2, and no restriction on option 3, e.g.
$sth->bind_param(':b1','Red');
$sth->bind_param(':b2','Wood');
$sth->bind_param(':b3','');
$sth->bind_param(':b4','');
$sth->bind_param(':b5','');
To apply restriction only on option 2, e.g.
$sth->bind_param(':b1','');
$sth->bind_param(':b2','Wood');
$sth->bind_param(':b3','');
$sth->bind_param(':b4','');
$sth->bind_param(':b5','');
This allows you to have a static statement, and the only thing that needs to change is the values supplied for the bind parameters.
It's also possible to use a NULL value to represent "no restriction" for an option, but you'd need to modify the SQL statement to do a "no restriction" when a NULL value is supplied. Either check for the NULL value specifically, e.g.
HAVING ( `values` LIKE CONCAT('%',:b1,'%') OR :b1 IS NULL )
AND ( `values` LIKE CONCAT('%',:b2,'%') OR :b2 IS NULL )
AND ( `values` LIKE CONCAT('%',:b3,'%') OR :b3 IS NULL )
-or- just convert the NULL to an empty string for use in the LIKE predicate, e.g.
HAVING `values` LIKE CONCAT('%',IFNULL(:b1,''),'%')
AND `values` LIKE CONCAT('%',IFNULL(:b2,''),'%')
AND `values` LIKE CONCAT('%',IFNULL(:b3,''),'%')
The same technique will work with a WHERE clause as well. You may want to consider whether a WHERE clause is more appropriate in your case. (We can't tell from the information provided.)
But note that the HAVING clause does not restrict which rows are included in the statement; rather, the HAVING clause only restricts which rows from the result set are returned. The HAVING clause is applied nearly last in the execution plan (I think its followed only by the ORDER BY and LIMIT.
The HAVING clause can apply to aggregates, which the WHERE clause cannot do. The HAVING clause can reference columns in the SELECT list, which the WHERE clause cannot do.
(Also note that VALUES is a reserved word, if it's not qualified (preceded by alias., it may need to be enclosed in backticks.)
No, you should not build the HAVING clause.
Yes, you could build the WHERE clause as you suggested.
HAVING is used for imposing conditions involving aggregation functions on groups.
This approach make sense for where more conditions are involved, but you can use them on 5 as well. Collect your menu values into a temp table and then inner join them to your query on LIKE condition
SELECT .... FROM MyTable
INNER JOIN MyTempTable
ON values LIKE '%' + MenuValue '%'