How to define a constant value in MySQL query - mysql

I need to run some scripts for MySQL databases. And I need to adjust the value in queries each time I run them. However, I need to specify the same value in two different places. I don't want to accidentally leave one value unchanged. Is there a way to specify the value as a constant in the script? I only have read privilege in the databases. I couldn't find the information through searching. Thanks.

You could probably do what you want with MySQL #variables, or via a simple select statement as your first table and join with no ON clause (thus a cross-join), but a Cartesian result with only 1 record will never create duplicates. Then you can use that column consistently throughout. For example...
select
from
( select #someNumber := 123,
#someDate := '2017-07-23' ) sqlVars,
OtherTable OT
where
OT.SomeNumberField = #someNumber
OR OT.OtherDateField = #someDate
but you can probably do similar as just a column such as
select
from
( select 123 as someNumber,
'2017-07-23' as someDate ) sqlVars,
OtherTable OT
where
OT.SomeNumberField = sqlVars.someNumber
OR OT.OtherDateField = sqlVars.someDate
Of course, standard join, left-join, etc to multiple tables should be able to see the columns either way with the table as the first in the list so it is visible all down-stream.

If you define a variable at the beginning of your query, you can use that throughout.

Related

Union query to combine results of 3 tables

I am relatively new to coding so please have patience.
I am trying to combine data from 3 tables. I have managed to get some data back but it isn't what i need. Please see my example below.
select oid, rrnhs, idnam, idfnam, dte1, ta
as 'access type' from person
left join
(select fk_oid, min(dte), dte1, ta
from
((Select fk_oid,min(accessdate) as dte, accessdate1 as dte1, accesstype as ta
from vascularpdaccess
where isnull(accesstype)=false group by fk_oid)
union
(Select fk_oid, min(hpdate) as dte, hpdate as dte1, HPACCE as ta
from hdtreatment
where isnull(hptype)=false group by fk_oid)) as bla
group by fk_oid) as access
on person.oid=access.fk_oid
where person.rrnhs in (1000010000, 2000020000, 3000030000)
My understanding with a union is that the columns have to be of the same data type but i have two problems. The first is that accesstype and hpacce combine in to a the same column as expected, but i dont want to actually see the hpacce data (dont know if this is even possible).
Secondly, the idea of the query is to pull back a patients 'accesstype' date at the first date of hpdate.
I dont know if this even makes sens to you guys but hoping someone can help..y'all are usually pretty nifty!
Thanks in advance!
Mikey
All queries need to have the same number of columns in the SELECT statement. It looks like you first query has the max number of columns, so you will need to "pad" the other to have the same number of columns. You can use NULL as col to create the column with all null values.
To answer the question (I think) you were asking... for a UNION or UNION ALL set operation, you are correct: the number of columns and the datatypes of the columns returned must match.
But it is possible to return a literal as an expression in the SELECT list. For example, if you don't want to return the value of HPACCE column, you can replace that with a literal or a NULL. (If that column is character datatype (we can't tell from the information provided in the question), you could use (for example) a literal empty string '' AS ta in place of HPACCE AS ta.
SELECT fk_oid
, MIN(HPDATE) AS dte
, hpdate AS dte1
, NULL AS ta
-- -------------------- ^^^^
FROM hdtreatment
Some other notes:
The predicate ISNULL(foo)=FALSE can be more simply expressed as foo IS NOT NULL.
The UNION set operator will remove duplicate rows. If that's not necessary, you could use a UNION ALL set operator.
The subsequent GROUP BY fk_oid operation on the inline view bla is going to collapse rows; but it's indeterminate which row the values from dte1 and ta will be from. (i.e. there is no guarantee those values will be from the row that had the "minimum" value of dte.) Other databases will throw an exception/error with this statement, along the lines of "non-aggregate in SELECT list not in GROUP BY". But this is allowed (without error or warning) by a MySQL specific extension to GROUP BY behavior. (We can get MySQL to behave like other databases and throw an error of we specify a value for sql_mode that includes ONLY_FULL_GROUP_BY (?).)
The predicate on the outer query doesn't get pushed down into the inline view bla. The view bla is going to materialized for every fk_oid, and that could be a performance issue on large sets.
Also, qualifying all column references would make the statement easier to read. And, that will also insulate the statement from throwing an "ambiguous column" error in the future, when a column named (e.g.) ta or dte1 is added to the person table.

Using WHERE clauses in an UPDATE statement

I have an update statement that's like this:
update user_stats set
requestsRecd = (select count(*) from requests where requestedUserId = 1) where userId = 1,
requestsSent = (select count(*) from requests where requesterUserId = 2) where userId = 2;
What I'm trying to do, is update the same table, but different users in that table, with the count of friend requests received for one user, and the count of friend requests sent by another user.
What I'm doing works if I remove the where clauses, but then, that updates all the users in the entire table.
Any idea how I can do something like this with the where clauses in there or achieve the same results using another approach?
(As proposed in several other answers, obviously, you could run two separate statements, but to answer the question you asked, whether it was possible, and how to do it...)
Yes, it is possible to accomplish the update operation with a single statement. You'd need conditional tests as part of the statement (like the conditions in the WHERE clauses of your example, but those conditions can't go into a WHERE clause of the UPDATE statement.
The big restriction we have with doing this in one UPDATE statement is that the statement has to assign a value to both of the columns, for both rows.
One "trick" we can make use of is assigning the current value of the column back to the column, e.g.
UPDATE mytable SET mycol = mycol WHERE ...
Which results in no change to what's stored in the column. (That would still fire BEFORE/AFTER update trigger on the rows that satisfy the WHERE clause, but the value currently stored in the column will not be changed.)
So, we can't conditionally specify which columns are to be updated on which rows, but we can include a condition in the expression that we're assigning to the column. As an example, consider:
UPDATE mytable SET mycol = IF(foo=1, 'bar', mycol)
For rows where foo=1 evaluates to TRUE, we'll assign 'bar' to the column. For all other rows, the value of the column will remain unchanged.
In your case, you want to assign a "new" value to a column if a particular condition is true, and otherwise leave it unchanged.
Consider the result of this statement:
UPDATE user_stats t
SET t.requestsRecd = IF(t.userId=1, expr1, t.reqestsRecd)
, t.requestsSent = IF(t.userId=2, expr2, t.reqestsSent)
WHERE t.userId IN (1,2);
(I've omitted the subqueries that return the count values you want to assign, and replaced that with the "expr1" and "expr2" placeholders. This just makes it easier to see the pattern, without cluttering it up with more syntax, that hides the pattern.)
You can replace expr1 and expr2 in the statement above with your original subqueries that return the counts.
As an alternative form, it's also possible to return those counts on a single row, using in an inline view (aliased as v here), and then specify a join operation. Something like this:
UPDATE user_stats t
CROSS
JOIN ( SELECT (select count(*) from requests where requestedUserId = 1) AS c1
, (select count(*) from requests where requesterUserId = 2) AS c2
) v
SET t.requestsRecd = IF(t.userId=1, v.c1 ,t.reqestsRecd)
, t.requestsSent = IF(t.userId=2, v.c2 ,t.reqestsSent)
WHERE t.userId IN (1,2)
Since the inline view returns a single row, we don't need any ON clause or predicates in the WHERE clause. (*I typically include the CROSS keyword here, but it could be omitted without affecting the statement. My primary rationale for including the CROSS keyword is to make the intent clear to a future reader, who might be confused by the omission of join predicates, expecting to find some in the ON or WHERE clause. The CROSS keyword alerts the reader that the omission of join predicates was intended.)
Also note that the statement would work the same even if we omitted the predicates in the WHERE clause, we could spin through all the rows in the entire table, and only the rows with userId=1 or userId=2 would be affected. (But we want to include the WHERE clause, for improved performance; there's no reason for us to obtain locks on rows that we don't want to modify.)
So, to summarize: yes, it is possible to perform the sort of conditional update of two (or more) rows within a single statement. As to whether you want to use this form, or use two separate statements, that's up for you to decide.
What you're trying to do is two updates try splitting these out:
update user_stats set
requestsRecd = (select count(*) from requests where requestedUserId = 1) where userId = 1;
update user_stats set
requestsSent = (select count(*) from requests where requesterUserId = 2) where userId = 2;
There may be a way using CASE statements to dynamically chose a column but I'm not sure if that's possible.
You are trying to update two different rows at the same time. That is not possible. Use two update queries then.
update user_stats set
requestsRecd = (select count(*) from requests where requestedUserId = 1) where userId = 1;
and
update user_stats set
requestsSent = (select count(*) from requests where requesterUserId = 2) where userId = 2;
Tell me if that works or not.

Getting different results from group by and distinct

this is my first post here since most of the time I already found a suitable solution :)
However this time nothing seems to help properly.
Im trying to migrate information from some mysql Database I have just read-only access to.
My problem is similar to this one: Group by doesn't give me the newest group
I also need to get the latest information out of some tables but my tables have >300k entries therefore checking whether the "time-attribute-value" is the same as in the subquery (like suggested in the first answer) would be too slow (once I did "... WHERE EXISTS ..." and the server hung up).
In addition to that I can hardly find the important information (e.g. time) in a single attribute and there never is a single primary key.Until now I did it like it was suggested in the second answer by joining with subquery that contains latest "time-attribute-entry" and some primary keys but that gets me in a huge mess after using multiple joins and unions with the results.
Therefore I would prefer using the having statement like here: Select entry with maximum value of column after grouping
But when I tried it out and looked for a good candidate as the "time-attribute" I noticed that this queries give me two different results (more = 39721, less = 37870)
SELECT COUNT(MATNR) AS MORE
FROM(
SELECT DISTINCT
LAB_MTKNR AS MATNR,
LAB_STG AS FACH,
LAB_STGNR AS STUDIENGANG
FROM
FKT_LAB
) AS TEMP1
SELECT COUNT(MATNR) AS LESS
FROM(
SELECT
LAB_MTKNR AS MATNR,
LAB_STG AS FACH,
LAB_STGNR AS STUDIENGANG,
LAB_PDATUM
FROM
FKT_LAB
GROUP BY
LAB_MTKNR,
LAB_STG,
LAB_STGNR
HAVING LAB_PDATUM = MAX(LAB_PDATUM)
)AS TEMP2
Although both are applied to the same table and use "GROUP BY" / "SELECT DISTINCT" on the same entries.
Any ideas?
If nothing helps and I have to go back to my mess I will use string variables as placeholders to tidy it up but then I lose the overview of how many subqueries, joins and unions I have in one query... how many temproal tables will the server be able to cope with?
Your second query is not doing what you expect it to be doing. This is the query:
SELECT COUNT(MATNR) AS LESS
FROM (SELECT LAB_MTKNR AS MATNR, LAB_STG AS FACH, LAB_STGNR AS STUDIENGANG, LAB_PDATUM
FROM FKT_LAB
GROUP BY LAB_MTKNR, LAB_STG, LAB_STGNR
HAVING LAB_PDATUM = MAX(LAB_PDATUM)
) TEMP2;
The problem is the having clause. You are mixing an unaggregated column (LAB_PDATUM) with an aggregated value (MAX(LAB_PDATAUM)). What MySQL does is choose an arbitrary value for the column and compare it to the max.
Often, the arbitrary value will not be the maximum value, so the rows get filtered. The reference you give (although an accepted answer) is incorrect. I have put a comment there.
If you want the most recent value, here is a relatively easy way:
SELECT COUNT(MATNR) AS LESS
FROM (SELECT LAB_MTKNR AS MATNR, LAB_STG AS FACH, LAB_STGNR AS STUDIENGANG,
max(LAB_PDATUM) as maxLAB_PDATUM
FROM FKT_LAB
GROUP BY LAB_MTKNR, LAB_STG, LAB_STGNR
) TEMP2;
It does not, however, affect the outer count.

What does it mean by select 1 from table?

I have seen many queries with something as follows.
Select 1
From table
What does this 1 mean, how will it be executed and, what will it return?
Also, in what type of scenarios, can this be used?
select 1 from table will return the constant 1 for every row of the table. It's useful when you want to cheaply determine if record matches your where clause and/or join.
SELECT 1 FROM TABLE_NAME means, "Return 1 from the table". It is pretty unremarkable on its own, so normally it will be used with WHERE and often EXISTS (as #gbn notes, this is not necessarily best practice, it is, however, common enough to be noted, even if it isn't really meaningful (that said, I will use it because others use it and it is "more obvious" immediately. Of course, that might be a viscous chicken vs. egg issue, but I don't generally dwell)).
SELECT * FROM TABLE1 T1 WHERE EXISTS (
SELECT 1 FROM TABLE2 T2 WHERE T1.ID= T2.ID
);
Basically, the above will return everything from table 1 which has a corresponding ID from table 2. (This is a contrived example, obviously, but I believe it conveys the idea. Personally, I would probably do the above as SELECT * FROM TABLE1 T1 WHERE ID IN (SELECT ID FROM TABLE2); as I view that as FAR more explicit to the reader unless there were a circumstantially compelling reason not to).
EDIT
There actually is one case which I forgot about until just now. In the case where you are trying to determine existence of a value in the database from an outside language, sometimes SELECT 1 FROM TABLE_NAME will be used. This does not offer significant benefit over selecting an individual column, but, depending on implementation, it may offer substantial gains over doing a SELECT *, simply because it is often the case that the more columns that the DB returns to a language, the larger the data structure, which in turn mean that more time will be taken.
If you mean something like
SELECT * FROM AnotherTable
WHERE EXISTS (SELECT 1 FROM table WHERE...)
then it's a myth that the 1 is better than
SELECT * FROM AnotherTable
WHERE EXISTS (SELECT * FROM table WHERE...)
The 1 or * in the EXISTS is ignored and you can write this as per Page 191 of the ANSI SQL 1992 Standard:
SELECT * FROM AnotherTable
WHERE EXISTS (SELECT 1/0 FROM table WHERE...)
it does what it says - it will always return the integer 1. It's used to check whether a record matching your where clause exists.
select 1 from table is used by some databases as a query to test a connection to see if it's alive, often used when retrieving or returning a connection to / from a connection pool.
The result is 1 for every record in the table.
To be slightly more specific, you would use this to do
SELECT 1 FROM MyUserTable WHERE user_id = 33487
instead of doing
SELECT * FROM MyUserTable WHERE user_id = 33487
because you don't care about looking at the results. Asking for the number 1 is very easy for the database (since it doesn't have to do any look-ups).
Although it is not widely known, a query can have a HAVING clause without a GROUP BY clause.
In such circumstances, the HAVING clause is applied to the entire set. Clearly, the SELECT clause cannot refer to any column, otherwise you would (correct) get the error, "Column is invalid in select because it is not contained in the GROUP BY" etc.
Therefore, a literal value must be used (because SQL doesn't allow a resultset with zero columns -- why?!) and the literal value 1 (INTEGER) is commonly used: if the HAVING clause evaluates TRUE then the resultset will be one row with one column showing the value 1, otherwise you get the empty set.
Example: to find whether a column has more than one distinct value:
SELECT 1
FROM tableA
HAVING MIN(colA) < MAX(colA);
If you don't know there exist any data in your table or not, you can use following query:
SELECT cons_value FROM table_name;
For an Example:
SELECT 1 FROM employee;
It will return a column which contains the total number of rows & all rows have the same constant value 1 (for this time it returns 1 for all rows);
If there is no row in your table it will return nothing.
So, we use this SQL query to know if there is any data in the table & the number of rows indicates how many rows exist in this table.
If you just want to check a true or false based on the WHERE clause, select 1 from table where condition is the cheapest way.
This means that You want a value "1" as output or Most of the time used as Inner Queries because for some reason you want to calculate the outer queries based on the result of inner queries.. not all the time you use 1 but you have some specific values...
This will statically gives you output as value 1.
I see it is always used in SQL injection,such as:
www.urlxxxxx.com/xxxx.asp?id=99 union select 1,2,3,4,5,6,7,8,9 from database;
These numbers can be used to guess where the database exists and guess the column name of the database you specified.And the values of the tables.
it simple means that you are retrieving the number first column from table ,,,,means
select Emply_num,Empl_no From Employees ;
here you are using select 1 from Employees;
that means you are retrieving the Emply_num column.
Thanks
The reason is another one, at least for MySQL. This is from the MySQL manual
InnoDB computes index cardinality values for a table the first time that table is accessed after startup, instead of storing such values in the table. This step can take significant time on systems that partition the data into many tables. Since this overhead only applies to the initial table open operation, to “warm up” a table for later use, access it immediately after startup by issuing a statement such as SELECT 1 FROM tbl_name LIMIT 1
This is just used for convenience with IF EXISTS(). Otherwise you can go with
select * from [table_name]
Image In the case of 'IF EXISTS', we just need know that any row with specified condition exists or not doesn't matter what is content of row.
select 1 from Users
above example code, returns no. of rows equals to no. of users with 1 in single column

mysql IF Else Statement

Not sure how far a sql query can go with if/else statements.
I have a simple SELECT statement:
SELECT amount, transtype FROM
transactions
The transtype column is going to be a number.
For example, 1 = sale, 2 = refund, 3 = error, 4 = canceled, 5 = something else.... and so on.
So, nothing complicated. But the list tends to grow for reporting reasons. Which is fine.
For a specific query I'm working on, is there a way to extract that column as one of 2 or three specified numbers or text?
For example, some transtype numbers are a 'loss', while others are a 'gain', and maybe others are 'neutral'.
I'd like to extract that column with only those 3, without using php inside the html table I'm throwing the rows into.
If my explanation is not clear, my apologies. It was hard to spit out.
Use the MySQL CASE() function for a fixed number of arguments. If the list is getting big, you should use a second table and join them.
Example:
SELECT CASE WHEN 1>0 THEN 'true' ELSE 'false' END;
Try joining against another table that contains the transaction types. Something like:
TRANSACTION_TYPES
transtype | number
label | varchar(32)
Then modify your query to include the join:
select t.amount, t.transtype, l.label
from transactions.t
join transaction_types l on t.transtype = l.transtype;
The ELT function should do the trick:
SELECT ELT(`transtype`, 'loss', 'loss', 'gain', 'neutral', …) FROM …
Not very elegant though, and logically I would do this in the view logic, not the database logic.
You should probably use ENUM type for that column. It's limited to 64 values, however if you would need more then you should create a new table and JOIN it in query.