MySQL get column names from a query - mysql

I want to query the column names from a query, something like this:
SHOW COLUMNS FROM
(SELECT `f1`, `f2`, `g1`, `g3` FROM `f` INNER JOIN `g` ON `g`.`Id` = `f`.`Id`)
And the result be
FIELD
f1
f2
g1
g3
Of course this query is wrong. But is there a way to do that?

Why do you need this?
If you want to use it in a procedure or similar, then you are already typed in the column names. If it is a dynamically generated query, use that method which generates the query to get the column names.
If you want to use them in your program, most languages provides functionality to get the resultsets column information.
PDOStatement::getColumnMeta ( int $column ) in PHP with PDO
OdbcDataReader.GetSchemaTable() in .NET
This solution is a hack! (and not recommended and also not tested, just a suggestion!)
Create a temporary table based on the query (SELECT .... INTO #temptable) (possibly with a where clause which never evaulates to true (WHERE 0=1), then query that temp tables metadata from INFORMATION_SCHEMA.COLUMNS

Related

SQL : store the string function on a column and use it later in where clause

How can we store the value of a string function applied on a column value and use it later in where clause, ex:
select trim(lower(column)) as c from Table where c like '%pattern%' or c like '%pattern2%';
instead of repeating trim(lower(column)) in each where...like clause I want to compute it once and use it as above. The above statement throws an error about unknown column c
This question has been asked many times, for example Using column alias in WHERE clause of MySQL query produces an error
I'll try to add value by describing all the alternatives I can think of.
You can repeat the expression in your WHERE clause:
select trim(lower(column)) as c from Table
where trim(lower(column)) like '%pattern%'
or trim(lower(column)) like '%pattern2%';
You can define the alias in a derived table subquery, then reference the alias in the outer query:
select c from (
select trim(lower(column)) as c from Table
) as t
where c like '%pattern%' or c like '%pattern2%';
Similarly, you could use a CTE, but in this case it doesn't have any advantage over the derived table.
with cte(c) as (
select trim(lower(column)) from Table
)
select c from cte
where c like '%pattern%' or c like '%pattern2%';
You could use the HAVING clause. MySQL has a non-standard feature to support references to alias in the HAVING clause. I don't usually recommend this, because it may not work in other SQL brands, and I prefer to use HAVING only for its standard purpose of filtering groups, not filtering rows.
select trim(lower(column)) as c from Table
having c like '%pattern%'
or c like '%pattern2%';
You could define a generated column for the expression you want, then use the generated column by name in your WHERE clause:
alter table Table add column c varchar(...) as (trim(lower(column)));
select c from Table
where c like '%pattern%' or c like '%pattern2%';
I will also comment that your example expression is not needed.
The use of LOWER() is probably not needed, because default collations are case-insensitive.
The TRIM() is also probably redundant, because the patterns in your example are bounded by wildcards.
So you can run the query you show simply:
select column from Table
where column like '%pattern%' or column like '%pattern2%';
You might ask "which solution has best performance?" (everyone asks about performance.) The answer is they all have bad performance in this example because the LIKE conditions have leading wildcards, and therefore the query will do a table-scan regardless.

Performing a custom SQL query

I want to select all those rows in table A where column x's value is present in table B's column y.
I am new to writing SQL queries have have tried using different combinations of SELECT statement, COUNT function and WHERE clause since are a really long time, but was unable to do so.
Is it possible to do this using plain SQL queries or is something complex like procedure needed?
A typical method is exists:
select a.*
from a
where exists (select 1
from b
where b.y = a.x
);

Access columns within exists clause

select *
from AllUK
where exists (select * from AllCompanies where replace(AllUK.mobile,' ','')=replace(AllCompanies.mobile,' ',''))
I need to include the columns from the AllCompanies table in to my first select. How can I do that?
select *
from AllUK a
join AllCompanies b
on a.mobile = b.mobile
exists is a boolean operation, so the clause you have above will always return all the results if there any records that can be joined accross the 2 tables. It's hard to tell what you're really trying to achieve.
Also, putting string operations on columns within exists and joins is not best practice because the compiler has to do the operation on every row & column at run time. Might be better to create a temp table to hold the replaced values and then join on that.

MySql Duplicated rows string comparation performance

I have a table with more then 2 million records,
I need to find duplication records in column with string type additionaly I have index for this field.
I have next query:
select m.* from member as m
where lower(m.username) in
(select lower(b.username) from member as b
where b.Username like 'a%'
group by b.username
having count(b.username) >= 2);
sub-query return only 4 records less then 0.2 seconds, but if I use them in where conditions section, this query working very long time and never return results....
I have tried to run next query, that theoretically the same logic:
select * from member as m where lower(Username) in (lower('a1'),
lower('a2'),lower('a3'),lower('a4'));
and it works fine and fast.
what is the issues ?
additionally I would like to run query with out where b.Username like 'a%' part?
In common case MySQL can not use index for IN subqueries
This is sad, but, actually, MySQL can not recognize "constant subqueries". What does it mean? It means that if you have a subquery that returns static list of values - and you use that in IN within another query, MySQL will not use index (by range).
Why it is so?
Actually, the most correct point is - because MySQL treats following queries:
.. WHERE `field` IN ('foo', 'bar', 'baz')
and
.. WHERE `field` IN (SELECT `col` FROM t)
-as different queries (I'm assuming that column col in table t in second query have same values, i.e. 'foo', 'bar', 'baz'). First query is equivalent for it's "expected" case, i.e. for range of values. But second query is equal for = ANY subquery - and so MySQL will not use index for that.
What to do
Actually, your case and cases similar to it - are cases when it's better to split your query into two parts. First part will be retrieve static list of values from your table. Second part will substitute result of your first part into IN clause and then you'll get index using.
Alternative - you can use JOIN syntax for table to itself. That may seems useful if you want to resolve an issue with one query (or if your list is too long)

Subquery for fetching table name

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.