Subquery for fetching table name - mysql

I have a query like this :
SELECT * FROM (SELECT linktable FROM adm_linkedfields WHERE name = 'company') as cbo WHERE group='BEST'
Basically, the table name for the main query is fetched through the subquery.
I get an error that #1054 - Unknown column 'group' in 'where clause'
When I investigate (removing the where clause), I find that the query only returns the subquery result at all times.
Subquery table adm_linkedfields has structure id | name | linktable
Currently am using MySQL with PDO but the query should be compatible with major DBs (viz. Oracle, MSSQL, PgSQL and MySQL)
Update:
The subquery should return the name of the table for the main query. In this case it will return tbl_company
The table tbl_company for the main query has this structure :
id | name | group
Thanks in advance.

Dynamic SQL doesn't work like that, what you created is an inline-view, read up on that. What's more, you can't create a dynamic sql query that will work on every db. If you have a limited number of linktables you could try using left-joins or unions to select from all tables but if you don't have a good reason you don't want that.
Just select the tablename in one query and then make another one to access the right table (by creating the query string in php).

Here is an issue:
SELECT * FROM (SELECT linktable FROM adm_linkedfields WHERE name = 'company') as cbo
WHERE group='BEST';
You are selecting from DT which contains only one column "linktable", then you cant put any other column in where clause of outer block. Think in terms of blocks the outer select is refering a DT which contains only one column.
Your problem is similar when you try to do:
create table t1(x1 int);
select * from t1 where z1 = 7; //error

Your query is:
SELECT *
FROM (SELECT linktable
FROM adm_linkedfields
WHERE name = 'company'
) cbo
WHERE group='BEST'
First, if you are interested in cross-database compatibility, do not name columns or tables after SQL reserved words. group is a really, really bad name for a column.
Second, the from clause is returning a table containing a list of names (of tables, but that is irrelevant). There is no column called group, so that is the problem you are having.
What can you do to fix this? A naive solution would be to run the subquery, run it, and use the resulting table name in a dynamic statement to execute the query you want.
The fundamental problem is your data structure. Having multiple tables with the same structure is generally a sign of a bad design. You basically have two choices.
One. If you have control over the database structure, put all the data in a single table, linktable for instance. This would have the information for all companies, and a column for group (or whatever you rename it). This solution is compatible across all databases. If you have lots and lots of data in the tables (think tens of millions of rows), then you might think about partitioning the data for performance reasons.
Two. If you don't have control over the data, create a view that concatenates all the tables together. Something like:
create view vw_linktable as
select 'table1' as which, t.* from table1 t union all
select 'table2', t.* from table2 t
This is also compatible across all databases.

Related

MySQL "exists" function in "where" clause

I would like to select a field only if the field exists on the table. If it does not exist, then I want to select a different field in the "where" clause.
The use case for this is that I have several similar tables, some of which have a "user_id" field and others do not, but rather than hard-coding all the tables that have this, I want to just check for the existence of the field, so that I can use the same sql for all. Those that do not have user_id use the "id" field instead for my application.
This code gives me the error
Unknown column 'user_id' in 'where clause'
for the table that does not have a user_id field.
I am using MySQL 5.7
select id from users
where
if (
exists( select 1 from information_schema.columns where table_name = 'users' and column_name = 'user_id' ),
user_id > 10,
id > 10);
I don't think you can achieve what you're looking to do in the way you're looking to do it.
The interpreter has no expectation that you might only want to use one of these fields, and it correctly points out that one of them is not valid for that query.
I think you'd have a far easier time having a common field name for everything you're searching on.
Consider creating views that provide all the columns of the base table and map the varying column as user_id.
e.g.
CREATE VIEW uniform_user
AS
SELECT
id,
id as user_id,
...
You can then exploit these views rather than the base tables and know that a user_id will definitely exist in each.
SELECT user_id FROM uniform_user
etc.

Is SELECT * clear enough to specify which query we are referring to?

I am new to mySQL.
I am following Mosh's tutorial to familiarize myself to SQL.
Here's my question for the following code.
SELECT *
FROM order_items
WHERE order_id = 6 AND unit_price*quantity > 30
When I looked up about SELECT *, it says: * means to return all all columns of the queried tables. Then I think SELECT * means that it grabs all tables from all schema.
My question is: Isn't it a bit inefficient and confusing to return all column provided my understanding is right? If the database become bigger and bigger, it will consume unnecessary effort to look up the keyword, so I think SELECT should specify what table it is referring to. Thanks for reading! 🥰
SELECT * does not fetch all tables from all schema. It only fetches the columns from the table you reference in your FROM clause. It only fetches the rows that match your WHERE clause.
The mistake is understandable given this statement in the MySQL documentation:
A select list consisting only of a single unqualified * can be used as shorthand to select all columns from all tables:
SELECT * FROM t1 INNER JOIN t2 ...
What they mean by "all tables" is only all tables referenced in this query. And only those in FROM or JOIN clauses. Not all tables everywhere.

use column names from inner query select the columns to display in outer query

My scenario is as follows( in MySQL)
I have a table say table 1, which has 2 columns:
userID, column_acess
Table 2 which has a list of columns say col1,col2,col3, etc.
Now What I would like to do is use pymySQL to query table 1 for the columns a particular userID is allowed to acesss, by inspecting the column, acess field ( which will contain a comma seperated list of columns in tabl2), and use that result in another sql query ( which works on table2) to actually get the data from the respective columns a user is allowed to acess.
So essentially I would like something like:
Select (Select column_acess from tabl1 where user_ID='123') from table2
So inner query should return the list of columns say col1, col2, which would be used to select the columns in the outer query in table2
How do I do that in mySQL?
I strongly encourage you too read this post. You should either first store columns in variable or use dynamic sql query. Use SELECT result as COLUMN name in other SELECT
BTW your schema is not even in 1 NF since you don’t have atomic values in table 1. You should avoid that.
MySQL supports the granting of column-level privileges to users, using the standard grant statement.
I would suggest that you start with the documentation on this subject.
An alternative to using grant for columns is to create views for different user types. This is, in fact, the more general solution, because the views can filter rows as well as columns. The idea is that the underlying tables are not directly accessible. The views are, so all access needs to go through the views.

Finding the differences in two tables

I have two large tables in a database. They both contain a column called "name". My goal is to locate rows that contain names that are in one database but not the other.
I'm guessing there will be a join statement and a where, but I cannot figure out how to use the two in tandem in order to create a successful query.
Suggestions?
SELECT * FROM TABLE_A WHERE NAME NOT IN
( SELECT NAME FROM TABLE_B )
EXISTS might be faster than IN, see Difference between EXISTS and IN in SQL?.
You can use EXISTS like this. It's useful to know both approaches since they are not exactly equal. You can swap the EXISTS quantifier for SOME, ALL or ANY. I think you can figure out what would happen :)
select * from a1 where not exists(select 1 from a2 where name=a1.name);
Note that they are not 100% equal! SQL has three-valued logic!

mysql group by performance tuning

select field1,count(*) from table where $condition group by field1
select field2,count(*) from table where $condition group by field2
Basically that's what I'm doing the job now,is there a way to optimize the performance so that MySQL doesn't need to search two times to group by for the where clause?
If the table is large and $condition is 'rare', it might help to create a temporary table in memory. This way, you group it twice but filter it only once.
CREATE TEMPORARY TABLE temp ENGINE=MEMORY
select field1,field2 from table where $condition;
select field1,count(*) from temp group by field1;
select field1,count(*) from temp group by field2;
No magic bullet here... the key upon which the aggregation takes place is distinct, so SQL needs to iterated over two different lists. To make this a bit more evident, ask yourself how you would like the data returned: all field1 first, then all field 2, or intertwined, or possibly "pivoted" (but how?..)
To avoid an extra "trip" to the server we could get these two results set returned together, or we could even group the two, using UNION ALL (and being careful to add a prefix column to to know what is what), but this latter solution would end up taxing the server a bit more if anything.