Set then Select - mysql

Can somebody please help me with this simple (but I don't know how) task?
In order to avoid wasting time fixing every single line, I'm trying to do something like this
SET #auditor = a, b, c;
SELECT *
FROM Audits
WHERE auditor in (#auditor)
My query indeed is really long and this part "WHERE auditor in (#auditor)" will be repetitive. Therefore, doing some kind of Set first will make it faster to update when there are some changes in the work force (we hire "d" and/or "b" left)
Thanks in advance

I recommend using a temporary table at the top of your query instead:
CREATE TEMPORARY TABLE FilteredAudits AS (SELECT * FROM Audits WHERE Auditor IN (a,b,c))
Then replace Audits with FilteredAudits as needed.
The benefits to this approach is that you guarantee the filter will only be applied once, meaning the rest of the query will JOIN to the smallest data set possible, and you don't have to repeat your IN() filter throughout the query.

You can use an array variable and FIND_IN_SET
SET #auditors = 'a,b,c';
SELECT *
FROM Audits
WHERE FIND_IN_SET(auditor,#auditors)
Explanation:
FIND_IN_SET(auditor,#auditors) is true is auditor is found in #auditors and in this case it returns the index.
But I would consider saving the auditors in a separate table rather than in a variable to better keep track of them.

you cannot set a parameter like an array. you will have to do something like
set #auditor='a,b,c'
now if auditor is a number this should work but if it is anything other then you will have to create a funciton dbo.[tfn_SplitList] on your system you will use it like below
auditor in(select List from dbo.[tfn_SplitList](#auditor ,','))

Related

SQL newbie: execution order of subqueries?

Warning: This is a soft question, where you'll be answering to someone who has just started teaching himself SQL from the ground up. I haven't gotten my database software set up yet, so I can't provide tables to run queries against. Some patience required.
Warnings aside, I'm experimenting with basic SQL but I'm having a little bit of a rough time getting a clear answer about the inner workings of subqueries and their execution order within my query.
Let us say my query looks something like shit:
SELECT * FROM someTable
WHERE someFirstValue = someSecondValue
AND EXISTS (
SELECT * FROM someOtherTable
WHERE someTable.someFirstValue = someOtherTable.someThirdValue
)
;
The reason I'm here, is because I don't think I understand fully what is going on in this query.
Now I don't want to seem lazy, so I'm not going to ask you guys to "tell me what's going on here", so instead, I'll provide my own theory first:
The first row in someTable is checked so see if someFirstValue is the same as someSecondValue in that row.
If it isn't, it goes onto the second row and checks it too. It continues like this until a row passes this little inspection.
If a row does pass, it opens up a new query. If the table produced by this query contains even a single row, it returns TRUE, but if it's empty it returns FALSE.
My theory ends here, and my confusion begins.
Will this inner query now compare only the rows that passed the first WHERE? Or will it check all the items someTable and someOtherTable?
Rephrased; will only the rows that passed the first WHERE be compared in the someTable.someFirstValue = someOtherTable.someThirdValue subquery?
Or will the subquery compare all the elements from someTable to all the elements in someOtherTable regardless of which passed the first WHERE and which didn't?
UPDATE: Assume I'm using MySQL 5.5.32. If that matters.
The answer is that SQL is a descriptive language that describes the result set being produced from a query. It does not specify how the query is going to be run.
In your case the query has several options on how it might run, depending on the database engine, what the tables look like, and indexes. The query itself:
SELECT t.*
FROM someTable t
WHERE t.someFirstValue = t.someSecondValue AND
EXISTS (SELECT *
FROM someOtherTable t2
WHERE t.someFirstValue = t2.someThirdValue
);
Says: "Get me all columns from SomeTable where someFirstValue = someSecondValue and there is a corresponding row in someOtherTable where that's table column someThirdValue is the same as someFirstValue".
One possible way to approach this query would be to scan someTable and first check for the first condition. When the two columns match, then look up someFirstValue in an index on someOtherTable(someThirdValue) and keep the row if the values match. As I say, this is one approach, and there are others.

Is it safe to use select * in views?

I've grown quite fond of the usefulness of CREATE VIEW. It for instance allows me to have global and specific values through COALESCE(post.publish, profile.publish) so that if publish is NULL, the global value gets fetched instead.
The part I'm a bit curious about from both perfomance and logical perspective, is how I should use this alongside the existing table. Lets say I have a table:
CREATE TABLE post (
id INT,
profile_id INT,
name VARCHAR,
publish ENUM('TRUE', 'FALSE') NULL
)
Would a CREATE VIEW be best to run like:
CREATE VIEW post_info AS
SELECT post.*, COALESCE(post.publish, profile.publish) AS publish
FROM post
INNER JOIN profile
ON post.profile_id = profile.id
And only use post_info in SELECT cases, or:
CREATE VIEW post_info AS
SELECT post.id, COALESCE(post.publish, profile.publish) AS publish
FROM post
INNER JOIN profile
ON post.profile_id = profile.id
And JOIN post_info with post in SELECT when extra values are needed?
Please share your insights and thoughs regarding this. I would like to hear your input to positives and drawbacks of each solution. Can also be one I haven't mentioned.
It really depends on how you will use the views. It should be worth mentioning that there are two methods MySQL can process a query that refers to a view, and the method used depends on the view declaration's ALGORITHM clause.
For the lack of a better phrasing, I will reproduce the manual:
For [ALGORITHM =] MERGE, the text of a statement that refers to the view and the
view definition are merged such that parts of the view definition
replace corresponding parts of the statement.
For TEMPTABLE, the results from the view are retrieved into a
temporary table, which then is used to execute the statement.
For UNDEFINED, MySQL chooses which algorithm to use.
The MERGE algorithm usually allows faster processing of the final query, however there are many cases where MySQL is unable to use it (see the linked manual page for more details).
So the answer is: if your view is not defined with ALGORITHM = TEMPTABLE and if the wrapping query does not prevent the use of the MERGE algorithm, the version with SELECT *, and without an extra JOIN, is better.
Otherwise, if MERGE is not used, the second solution could be better.
As a side note, to adress the use case you mention, a better option would be to have your application layer fill the post.publish with the value in profile.publish at insertion time, and get rid of the JOIN as well as the view. Alternatively, the same effect can be achieved by placing a suitable trigger on the table.

SQL update query for balances using Access raises 'Operation must use an updateable query'

I have the following query (MS Access 2010) which I'm trying to use to update a table with a running balance:
UPDATE Accounts a SET a.CurrentBalance =
(SELECT sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount)) +
(select a1.openingbalance
from accounts a1 where a1.accountid = a.accountid) AS TotalAmount
FROM transactions t inner join (
transactiontypes tt inner join
categories c on c.categoryid = tt.categoryid)
on t.transactiontypeid = tt.transactiontypeid);
The tables used are:
A work around for the "Query must use an updateable query" is to use a temporary table and then update the final target data based on the aggregated data in the temporary table. In your case, as mwolfe suggests, you have an aggregate function in the inner select query. The workaround could provide a quick fix for this situation, as it has for me.
Ref: http://support.microsoft.com/kb/328828
This article helped me understand the specifics of the situation and provided the work around:
http://www.fmsinc.com/MicrosoftAccess/query/non-updateable/index.html
You cannot use aggregate functions (like SUM) in an update query. See Why is my query read-only? for a full list of conditions that will cause your query to be "non-updateable".
The Access db engine includes support for domain functions (DMax, DSum, DLookup, etc.). And domain functions can often allow you to circumvent non-updateable query problems.
Consider DSum() with these 3 rows of data in MyTable.
id MyNumber
1 2
2 3
3 5
Then in the Immediate window, here are 2 sample DSum() expressions.
? DSum("MyNumber", "MyTable")
10
? DSum("IIf(id=1,MyNumber * -1, MyNumber)", "MyTable")
6
I think you may be able to use something like that second expression as a replacement for the sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount) part of your query.
And perhaps you can use a DLookup() expression to get your TotalAmount value. Unfortunately I got frustrated trying to translate your current SQL to domain functions. And I realize this isn't a complete solution, but hope it will point you to something useful. If you edit your question to show us brief samples of the starting data and what you hope to achieve from your UPDATE statement based on that sample data, I would be willing to have another look at this.
Finally, consider whether you absolutely must store CurrentBalance in a table. As a rule of thumb, avoid storing derived values. Instead, use a SELECT query to compute the derived value when you need it. That approach would guarantee CurrentBalance is always up-to-date whenever you retrieve it. It would also spare you the effort to create a working UPDATE statement.

In SQL, how do I exclude result from SELECT * FROM ...?

I know my title is not very descriptive... let me explain in details here.
Let say, if a table has 26 fields. e.g. field_a ... field_z. and I only want a select query to return me 15 fields only.
So, normally, I will do SELECT field_a, field_b ... field_o FROM myTable.
Which is tedious. Is there a way in MYSQL that I can do a SELECT * and tell it not to return certain fields?
e.g. soemthing like SELECT * exclude (field_p, field_q .. field_z) FROM myTable?
Thanks all for the answers. :)
SELECT * is evil.
You should never use it in production code.
You should always specify the columns you want returned, like so:
SELECT `column1`, `column2`, `someothercolumn`
FROM `myTable`
As always, the documentation can help with the nitty gritty!
SQL itself does not support the functionality you are asking for. (If it did, it would have the same problems as select *. See other's comments)
This is what I do when I'm using MySQL:
Fire up mysql command line client
Perform a describe my_table
Copy the data in Field column (mouse select)
Paste data in my editor (TextPad)
Manually remove the columns I don't need
Run a macro that substitutes new line for a comma (and insert a table alias)
All in all, it takes around 20-30 seconds to create a select list regardless of the number of columns. This way ensures that I don't misspell or forget any columns.
delimiter //
DROP PROCEDURE IF EXISTS `getColumnNames`//
CREATE PROCEDURE `getColumnNames` (db_name CHAR(255), t_name CHAR(255), ex_name CHAR(255))
BEGIN
SELECT group_concat(column_name) FROM `information_schema`.`COLUMNS` C
WHERE
table_schema = db_name
AND
table_name = t_name
AND
column_name not in (ex_name)
GROUP BY table_schema,table_name;
END
//
delimiter ;
call getColumnNames("Db_name", "tbl_name", "col_to_exclude");
No use the fields you want (select * shouold not appear in production code even if you want allthe fields). I don't know about mySQL but in SQL Server I can drag and drop the columns which makes it easy to specify, is there a way to make this less tedious by dragging and dropping?
There's no way to do this because 'SELECT *' is a bad idea in a production environment anyhow. Just think of all the extra error-catching that would need to be done if something like this existed -- what if the excluded field didn't exist in the table? Does this create an error? A warning?
Enumerating all your fields is indeed tedious, but it's the correct way to do it. It makes your code (a little bit more) self-documenting, and helps stop errors early in the cycle. For one example, if 'user_name' is eventually renamed to 'username' for whatever reason, the SQL statement will fail and you won't have a strange data lolling around in your code waiting to be traced down.
I hope that there's none way to do this. Even if there is one, I'd suggest not to use it.
Kindof solution is to create a view that excludes the one row you want to have excluded, and you can select * from the view.

Mysql "magic" catch all column for select statement

Is there a way that I can do a select as such
select * from attributes where product_id = 500
would return
id name description
1 wheel round and black
2 horn makes loud noise
3 window solid object you can see through
and the query
select * from attributes where product_id = 234
would return the same results as would any query to this table.
Now obviously I could just remove the where clause and go about my day. But this involves editing code that I don't really want to modify so i'm trying to fix this at the database level.
So is there a "magical" way to ignore what is in the where clause and return whatever I want using a view or something ?
Even if it was possible, I doubt it would work. Both of those WHERE clauses expect one thing to be returned, therefore the code would probably just use the first row returned, not all of them.
It would also give the database a behaviour that would make future developers pull their hair out trying to understand.
Do it properly and fix the code.
or you could pass "product_id" instead of an integer, if there's no code checking for that...so the query would become:
select * from attributes where product_id = product_id;
this would give you every row in the table.
If you can't edit the query, maybe you can append to it? You could stick
OR 1=1
on the end.
You may be able to use result set metadata to get what you want, but a result set won't have descriptions of fields. The specific API to get result set metadata from a prepared query varies by programming language, and you haven't said what language you're using.
You can query the INFORMATION_SCHEMA for the products table.
SELECT ordinal_position, column_name, column_comment
FROM INFORMATION_SCHEMA.columns
WHERE table_name = 'products' AND schema_name = 'mydatabase';
You can restructure the database into an Entity-Attribute-Value design, but that's a much more ambitious change than fixing your code.
Or you can abandon SQL databases altogether, and use a semantic data store like RDF, which allows you to query metadata of an entity in the same way you query data.
As far out as this idea seems I'm always interested in crazy ways to do things.
I think the best solution I could come up with is to use a view that uses the products table to get all the products then the attributes table to get the attributes, so every possible product is accounted for and all will get the same result