Where to put aliases in MySQL query - mysql

I'm quite new to MySQL and SQL in general and can't fully understand how and where to place aliases.
For examlple I have scheema like this:
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE table_a (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
value INT NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE table_b (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
value INT NOT NULL,
PRIMARY KEY(id)
);
INSERT INTO table_a (value) VALUES (25), (43), (15);
INSERT INTO table_b (value) VALUES (11), (15), (16);
And I need to get sum of values of every record(in both tables). For given example it should be 125. My query looks like this
SELECT SUM(value) FROM
(
SELECT * FROM
(SELECT value FROM table_a) AS a_value
UNION ALL
(SELECT value FROM table_b) #AS b_value
) as total;
What I can not understand is:
why "a_value" and "total" aliases are absolutely necessary (I never use them in this query) and their absence giving me "Every derived table must have it's own alias"?
why "b_value" is making "SELECT is not valid at this position" error for first select? Isn't it also a derived table like one with "a_value" alias?
And I have one question about parentheses and subqueries: why do I need this "SELECT * FROM" to take UNION in parentheses? I started from such request:
SELECT SUM(value) FROM
(SELECT value FROM table_a) AS a_value
UNION ALL
(SELECT value FROM table_b);
But instead of sum I want it was giving me sum of "value" from "table_a" united with "value" column from "table_b" - [83, 11, 15, 16]. So I tried just take everything after FROM in parentheses and it didn't work out: I got "SELECT is not valid at this position" error.
I wasn't really sure, just tried first that came to my mind and put this SELECT * FROM inside parentheses before everything else and it worked. So I want to know why it works and why my first way (with just parentheses) does not work?

All derived tables -- subqueries in the FROM clause -- need to have a table alias. It doesn't make a difference if they are ever used.
However, the subqueries are not needed. You can put the UNION ALL in the FROM clause:
SELECT SUM(value) as total
FROM (SELECT value FROM table_a UNION ALL
SELECT value FROM table_b
) ab
-------^ note that this is needed although it is not used in the query
The alias could be used:
SELECT SUM(ab.value) as total
It might be slightly faster to do the aggregation first in the subquery:
SELECT SUM(value)
FROM (SELECT SUM(value) as value FROM table_a UNION ALL
SELECT SUM(value) as value FROM table_b
) ab

It is because the result from the sub-query can it can be uniquely identified. You have to do this even if you are not going to reference the name anywhere.
When subqueries are used in a SELECT statement they can only return one value.
In general, the subquery is run only once for the entire query, and its result reused.

Related

How to check if a column has all null values in a table? snowflake sql

I would like to see if in my table there exists a column where the entirety of its rows are null.
SELECT * FROM yourTableName
WHERE yourSpecificColumnName IS NULL
-> this will only return the values that are null but i wont know if yourSpecificColumnName is entirely null throughout the table
Using COUNT combined with HAVING:
COUNT
Returns either the number of non-NULL records for the specified columns, or the total number of records.
SELECT 'Entire_column_is_empty'
FROM yourTable
HAVING COUNT(yourSpecificColumnName) = 0;
or QUALIFY:
SELECT *
FROM yourTable
QUALIFY COUNT(yourSpecificColumnName) OVER() = 0;
Alternative approach:
SELECT 'Entire_column_is_empty'
FROM yourTable
HAVING MIN(yourSpecificColumnName) = MAX(yourSpecificColumnName);

Is it possible to query MySQL to get only fields that contain duplicate/repeating strings?

What I mean is, I have table with a "list" column. The data that goes into the "list" is related to addresses, so I sometimes get repeated zip codes for one record in that field.
For example, "12345,12345,12345,12456".
I want to know if it's possible to construct a query that would find the records that have an unknown string that duplicates within the field, such that I would get the records like "12345,12345,12345,12456", but not ones like "12345,45678,09876".
I hope that makes sense.
Yes, it is possible. You need to use a numbers table to convert your delimited string into rows, then use group by to find duplicates, e.g.
CREATE TABLE T (ID INT, List VARCHAR(100));
INSERT INTO T (ID, List)
VALUES (1, '12345,12345,12345,12456'), (2, '12345,45678,09876');
SELECT
T.ID,
SUBSTRING_INDEX(SUBSTRING_INDEX(T.list, ',', n.Number), ',', -1) AS ListItem
FROM T
INNER JOIN
( SELECT 1 AS Number UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5
) AS n
ON CHAR_LENGTH(T.list)-CHAR_LENGTH(REPLACE(T.list, ',', ''))>=n.Number-1
GROUP BY T.ID, ListItem
HAVING COUNT(*) > 1;
If you don't have a numbers table you can create one in a derived query as I have above with UNION ALL
Example on DB Fiddle
With that being said, this is almost certainly not the right way to store your data, you should instead use a child table, e.g.
CREATE TABLE ListItems
(
MainTableId INT NOT NULL, --Foreign Key to your current table
ItemName VARCHAR(10) NOT NULL -- Or whatever data type you need
);
Then your query is much more simple:
SELECT T.ID, li.ItemName
FROM T
INNER JOIN ListItems AS li
ON li.MainTableId = T.ID
GROUP BY T.ID, li.ItemName
HAVING COUNT(*) > 1;
If you need to recreate your original format, this is easily done with GROUP_CONCAT():
SELECT T.ID,
GROUP_CONCAT(li.ItemName) AS List
FROM T
INNER JOIN ListItems AS li
ON li.MainTableId = T.ID
GROUP BY T.ID;
Example on DB Fiddle
I am still unclear what your desired result is based on your question however if it is simply to get all rows where there is a duplicate entry in column list you could do the following:
SELECT * FROM TABLE
WHERE COLUMN IN
(SELECT COLUMN FROM TABLE
having count(*) >1)

mysql group by year() of timestamp and foreign_id order by timestamp

i have a table like
create table `my_table`
(
`id` int auto_increment primary key,
`foreign_id` int not null,
`some_data` text,
`some_timestamp` timestamp default CURRENT_TIMESTAMP not null
);
I need to get unique rows both per year(got from timestap) and foreing_id , and get it from table ordered by timestamp
i've tried query :
SELECT *
FROM (
SELECT *
FROM `my_table`
ORDER BY `some_timestamp` DESC) as lr
GROUP BY YEAR(lr.`some_timestamp`),lr.`foreign_id`;
but this one not ordering by timestamp, idk why.
Other query worked ok untill i've loaded it to other server:
SELECT * FROM `my_table`
WHERE `id`,`some_timestamp` IN
(
SELECT `id`,max(`some_timestamp`)
FROM `my_table` lr2
GROUP BY YEAR(lr2.`some_timestamp`),`lr2`.foreign_id
);
on my pc this query executes around 3 seconds, on other server it get's error 504 gateway timeout.
Please help me to find best solution for this task.
You're making things too complicated. It didn't order correctly because GROUP BY does an implicit sorting, when no ORDER BY is specified.
Simply do it like this:
SELECT some_timestamp, foreign_id, MAX(some_timestamp) AS most_recent_timestamp_per_year
FROM `my_table`
GROUP BY YEAR(some_timestamp), foreign_id
ORDER BY `some_timestamp` DESC
I'm not sure, what your final query is supposed to do.
Also note, that you don't use group by correctly.
Every column in the select clause must either be specified in the group by clause or have an aggregate function applied to it. Otherwise you get random rows per group.
To order this query by date you need to rewrite it like this:
SELECT *
FROM (
SELECT *
FROM `my_table`) as lr
GROUP BY YEAR(lr.`some_timestamp`),lr.`foreign_id`
ORDER BY YEAR(lr.`some_timestamp`) DESC;
About execution time for second query.
If you don't want to dig in, try to add index on server DB to columns some_timestamp and foreign_id. It probably speed up the execution time.
Try something like this in mysql shell:
`CREATE INDEX my_request ON my_table (foreign_id, some_timestamp);
More about indexes you can find here

subquery or passthrough; SQL Server 2008r2

I have an Access 2000 query that works on linked tables from SQL Server 2008 R2. I need to write it as a view or pass through query:
`
SELECT Max(CLng((Mid([tbl1]![ID],5)))) AS lastnumberused
FROM [tbl1]
WHERE ((([tbl1]![ID]) Like "OODD*" And ([tbl1]![ID]) Not Like "OODDid*" And ([tbl1]![ID]) Not Like "*x") AND ((CLng((Mid([tbl1]![ID],5))))<1000000));
`
So what I'm looking for is the max number under 1000000 that begins with OODD% but not OODDID%. Also the record cannot end with 'x'.
My code in TSQL for SQL Server looks like this and doesn't work...
`
SELECT
convert (int, (substring(tblMain.BarcodeID,5,10))) as X1, [ID]
FROM tblMain
WHERE ([tbl1]![ID] LIKE N'OODD%')
AND ([tbl1]![ID] NOT LIKE N'%x%')
AND ([tbl1]![ID] NOT LIKE N'OODDID%')
Select MAX (x1)+1
from bar1
where (x1<1000000)
`
suggestions?
thanks,H
Update 15june2013
WITH T1 (number)
AS
(SELECT substring(tbl1.ID,5,10)
FROM tblMain AS tbl1
WHERE (ID LIKE N'oodd%')
AND (ID NOT LIKE N'%x%')
AND (ID NOT LIKE N'ooddID%'))
SELECT (cast((number)as int)) FROM T1
This works and returns 561770 rows.
With T1(number)
as(SELECT cast(SUBSTRING(ID, 5, 7)as int)
FROM tblMain as tbl1
WHERE (tbl1.ID LIKE N'oodd%')
AND (tbl1.ID NOT LIKE N'%x%')
AND (tbl1.ID NOT LIKE N'%ooddID%')
AND ISNUMERIC(SUBSTRING(tbl1.ID,5,10))=1)
Select max(number) from T1
Also works but returns a number above 1000000
When a where statement is added the following code includes records that should have been excluded in the previous statement.
With T1(number)
as(SELECT cast(SUBSTRING(ID, 5, 7)as int)
FROM tblMain as tbl1
WHERE (tbl1.ID LIKE N'oodd%')
AND (tbl1.ID NOT LIKE N'%x%')
AND (tbl1.ID NOT LIKE N'%ooddID%')
AND ISNUMERIC(SUBSTRING(tbl1.ID,5,10))=1)
Select max(number) from T1
where x1 <1000000
The WHERE clause contains a [tbl1] table which is not present in the FROM clause. You should either change it to tblMainBee or add an "tbl1" alias in the FROM clause with AS.
Also, you have two unconnected SELECT statements, instead of a single one. Again, in the second SELECT, there is a bar1 table which doesn't appear anywhere else... is it correct?
Another error is that you cannot use "!" to separate table name and field name. You must use a dot.
Finally, there are issues when trying to convert the data type. You don't need it since comparison between strings also works.
Your access SELECT statement is much more straightforward.
Try with
WITH T1(number) AS (
SELECT substring(tbl1.ID,5,LEN(tbl1.ID)-4)
FROM tblMainBee as tbl1
WHERE ([tbl1].[ID] LIKE N'OODD%')
AND ([tbl1].[ID] NOT LIKE N'%x%')
AND ([tbl1].[ID] NOT LIKE N'OODDID%')
AND (IsNumeric(substring(tbl1.ID,5,LEN(tbl1.ID)-4)) = 1)
)
SELECT max(convert(bigint,number)) FROM T1 WHERE number < 1000000;
The second type conversion is implicit since 1000000 is numeric. The first one is necessary. Otherwise, it would consider '12346' bigger than '123421'.
Comparison between strings only works if they have fixed length.
Regards,
Try this
select MAX(CAST(SUBSTRING(ID,5,9) as INT))+1
from tblMainBee
where
ID Like 'OODD%'
AND ID Not Like 'OODDid%'
AND ID Not Like '%x'
AND ISNUMERIC(SUBSTRING(ID,5,9))=1
AND CAST(SUBSTRING(ID,5,9) as INT)<1000000

How to return NULL when result is empty?

I have a simple query that selects one field and only one row, thus one value.
Is there any way to make it return NULL if the query results in an empty set? Instead of returning zero rows?
I think I need to use something with NOT EXISTS, THEN NULL but not certain about it.
select
(Your entire current Select statement goes here) as Alias
from
dual
dual is a built in table with a single row that can be used for purposes like this. In Oracle this is mandatory. MySQL supports it, but you can also just select a single value without specifying a table, like so:
select
(Your entire current Select statement goes here) as Alias
In either case you're selecting a single value. This means that:
If your select returns one value, that value is returned.
If your select statement returns one column, but no rows, NULL will be returned.
If your select statement returns multiple columns and/or multiple rows, this won't work and the query fails.
An easy way to do this is with aggregation:
select max(col)
from t
where <your condition here>
This always returns one row. If there is no match, it returns NULL.
Late reply but I think this is the easiest method:
SELECT
IFNULL((SELECT your query), NULL)
Use a UNION with a NOT EXISTS(original where clause)
select col1
from mytable
where <some condition>
union
select null
where not exists (
select * from mytable
where <some condition>)
You can use COALESCE for example:
SELECT COALESCE(Field1,NULL) AS Field1 FROM Table1
Edit 1:
sorry i mistake with return field as null not result set,for result set return as null use Union and Exist Function like this:
SELECT NULL AS Field1 FROM Table1 WHERE not EXISTS(SELECT Field1 FROM Table1 WHERE Field2>0)
UNION
SELECT Field1 FROM Table1 WHERE Field2>0