SQL join row contents to another table's column names - mysql

Assuming I have the following two tables:
**Table A**
ID Day Month Year
------------------------
1 1 1 1900
3 13 3 2009
49 28 2 1984
**Table B**
ID ABC_1_1_1900 ABC_2_1_1900 ... ABC_31_12_2100
-------------------------------- ... ---------------
1 431 15449 98565
2
3 ....
.
.
n ....
and would like to get the following table:
**Table C**
ID ABC
------------------------
1 431
3 (value B.ABC_13_3_2009 for ID=3)
49 (value B.ABC_28_2_1984 for ID=49)
What essentially I'm trying to achieve is, get a subset of Table B by matching the row contents of A to the column names of B and inner joining on the IDs.
The equivalent would be
SELECT A.ID, B.CONCAT('ABC_', A.Day, '_', A.Month, '_', A.Year) AS ABC
FROM A
INNER JOIN B ON A.ID=B.ID
which unfortunately doesn't work. Any ideas greatly appreciated!

What you are trying to do is to create an SQL statement from a variable. How this is done depends on the SQL technology you are using. From the tag, I assume that you use MySQL. With MySQL you need to make a so called Prepared Statement (Attention: They say it's quite hacky, see: mysql field name from variable). With the prepared statement, you will be able to pass the column contents to your SQL statement. All this would look like the following:
Create SQL String:
SET #s = CONCAT('SELECT A.ID, B.ABC_', A.Day, '_', A.Month, '_', A.Year, ' AS ABC FROM A INNER JOIN B ON A.ID=B.ID');
Prepare Statement and execute:
PREPARE stmt FROM #s;
EXECUTE stmt;
Note: Be aware that this can be prone to errors, since the number in the column (for example day) must always be formatted the same as the number in the column name (ABC_X_Y_Z).

Related

I need to display the data, if at least one string present in another column using SQL

I have two columns in a table, column 1 contains tom jerry and column 2 has tom xxxx . I need to fetch the row at least one string is present the other column data
column1 column2
-----------------------------
tom jerry : tom xxx
tiger : tom yyy
tiger lion : lipard
lion tom : tiger lion
23 : 235 452
23 : 23
Expected result:
column1 column2
---------------------------
tom jerry : tom xxx
lion tom : tiger lion
23 : 23
Here is my approach:
Put both columns to another table with identity column (to identify each row)
Write a function to split text by a string (in this case pass a space)
Here you can use built in function if it is higher than SQL Server 2016
Get the identity and the splitted data to 2 tables each for column 1 and 2
Join the 2 tables for matches in Col-Split data, and get the IDs
Now Query for the data in table in above 1
Here is the Fiddle assuming that the code is for SQL Server 2016
In MySQL, you can use a regular expression for the data you have presented:
where column1 regexp replace(column2, ' ', '|') or
column2 regexp replace(column1, ' ', '|')
This may or may not work as stated. For instance, this will match "lion" and "lions" -- that meets what you are describing as I read it. That said, you can modify the patterns to enforce word breaks if you have something slightly different in mind.
You'd be able to achieve this by creating a function wherein you will pass value of column1 and column2. The function will do the heavy-lifting of checking if column1 value (multiple words separated by whitespace) has any of its word matching in column2 value. The pseudo code of function should be something like this -
Function bool IsCol1WordPresentInCol2(varchar2 col1value, varchar2 col2value)
Step 1: Split col1value based on whitespace to get series of words
Step 2: Split col2value based on whitespace to get series of words
Step 3: Outer loop to iterate through all words of col1 (extracted from step 1)
Step 4: Inner loop to iterate through all words of col2 (extracted from step 2)
Step 5: Check if the col1 and col2 words do match. At any point, if any match is found, return true from the function; else false.
Step 6: Based on the returned bool value from this function, you'd be able to identify, in your calling logic, if that record satisfies your condition.
You can use instring function to achieve this. Function depends on the database you use. Below is the query,
select * from table1 where col1 > col2;
select distinct t1.* from table1 t1
inner join table1 t2
on (instr(substring(t1.col1, instr(t1.col1, ' ') -1), col2) > 0);

select one row multiple time when using IN()

I have this query :
select
name
from
provinces
WHERE
province_id IN(1,3,2,1)
ORDER BY FIELD(province_id, 1,3,2,1)
the Number of values in IN() are dynamic
How can I get all rows even duplicates ( in this example -> 1 ) with given ORDER BY ?
the result should be like this :
name1
name3
name2
name1
plus I shouldn't use UNION ALL :
select * from provinces WHERE province_id=1
UNION ALL
select * from provinces WHERE province_id=3
UNION ALL
select * from provinces WHERE province_id=2
UNION ALL
select * from provinces WHERE province_id=1
You need a helper table here. On SQL Server that can be something like:
SELECT name
FROM (Values (1),(3),(2),(1)) As list (id) --< List of values to join to as a table
INNER JOIN provinces ON province_id = list.id
Update: In MySQL Split Comma Separated String Into Temp Table can be used to split string parameter into a helper table.
To get the same row more than once you need to join in another table. I suggest to create, only once(!), a helper table. This table will just contain a series of natural numbers (1, 2, 3, 4, ... etc). Such a table can be useful for many other purposes.
Here is the script to create it:
create table seq (num int);
insert into seq values (1),(2),(3),(4),(5),(6),(7),(8);
insert into seq select num+8 from seq;
insert into seq select num+16 from seq;
insert into seq select num+32 from seq;
insert into seq select num+64 from seq;
/* continue doubling the number of records until you feel you have enough */
For the task at hand it is not necessary to add many records, as you only need to make sure you never have more repetitions in your in condition than in the above seq table. I guess 128 will be good enough, but feel free to double the number of records a few times more.
Once you have the above, you can write queries like this:
select province_id,
name,
#pos := instr(#in2 := insert(#in2, #pos+1, 1, '#'),
concat(',',province_id,',')) ord
from (select #in := '0,1,2,3,1,0', #in2 := #in, #pos := 10000) init
inner join provinces
on find_in_set(province_id, #in)
inner join seq
on num <= length(replace(#in, concat(',',province_id,','),
concat(',+',province_id,',')))-length(#in)
order by ord asc
Output for the sample data and sample in list:
| province_id | name | ord |
|-------------|--------|-----|
| 1 | name 1 | 2 |
| 2 | name 2 | 4 |
| 3 | name 3 | 6 |
| 1 | name 1 | 8 |
SQL Fiddle
How it works
You need to put the list of values in the assignment to the variable #in. For it to work, every valid id must be wrapped between commas, so that is why there is a dummy zero at the start and the end.
By joining in the seq table the result set can grow. The number of records joined in from seq for a particular provinces record is equal to the number of occurrences of the corresponding province_id in the list #in.
There is no out-of-the-box function to count the number of such occurrences, so the expression at the right of num <= may look a bit complex. But it just adds a character for every match in #in and checks how much the length grows by that action. That growth is the number of occurrences.
In the select clause the position of the province_id in the #in list is returned and used to order the result set, so it corresponds to the order in the #in list. In fact, the position is taken with reference to #in2, which is a copy of #in, but is allowed to change:
While this #pos is being calculated, the number at the previous found #pos in #in2 is destroyed with a # character, so the same province_id cannot be found again at the same position.
Its unclear exactly what you are wanting, but here's why its not working the way you want. The IN keyword is shorthand for creating a statement like ....Where province_id = 1 OR province_id = 2 OR province_id = 3 OR province_id = 1. Since province_id = 1 is evaluated as true at the beginning of that statement, it doesn't matter that it is included again later, it is already true. This has no bearing on whether the result returns a duplicate.

How to perform union operation between two tables?

After performing union operator between two tables in which the columns in one of the table are empty the result is displaying from second row... i dont why why is this happening ...? can anyone clarify my doubt?
my tables are sample1 and sample2
Both table contains Id, Empname , Location
data in table is
101 Null NUll
102 aaaa sec
data in table2 is
103 bbbb hyd
102 cccc gdv
Query:
(select EmpName,Location
from sample1)
union
(select EmpName,Location
from sample2)
Output
EMPNAME LOCATION
aaaa sec
bbbb hyd
cccc gdv
To remove the null records from the result, try this:
(select EmpName,Location
from sample1
WHERE EmpName IS NOT NULL
AND Location IS NOT NULL)
union
(select EmpName,Location
from sample2
WHERE EmpName IS NOT NULL
AND Location IS NOT NULL)
Result:
EMPNAME LOCATION
vijay ngdv
suresh hyd
ajay hyd
See result in SQL Fiddle.
EDIT:
I guess the record contains empty string or white spaces instead of null. So try this:
(select EmpName,Location
from sample1
WHERE LENGTH(TRIM(EmpName)) >0
AND LENGTH(TRIM(Location)) >0)
union
(select EmpName,Location
from sample2
WHERE LENGTH(TRIM(EmpName)) >0
AND LENGTH(TRIM(Location)) >0)
See result in SQL Fiddle.
Explanation:
LENGTH(TRIM(EmpName)) will return the length of the field EmpName after removing white spaces from it.
How to perform union operation between two tables?
you have to use union operator with following circumstances,
1.You have similar information in multiple tables and you want to retrieve rows from all of
them at once.
2.You want to select several sets of rows from the same table, but the conditions that
characterize each set aren't easy to write as a single WHERE clause. UNION allows retrieval
of each set with a simpler WHERE clause in its own SELECT statement; the rows retrieved by
each are combined and produced as the final query result.
Note : its not the perfect answer for your question but it helps in understanding of union
operator
please see this http://www.mysqlfaqs.net/mysql-faqs/Funtions-and-Operators/How-does-union-work-in-MySQL

Accessing column value of table A using value from table B

I've two tables say ABC and XYZ,
Table ABC:
B C ID ABC_ID
====================
50 30 10 2
60 31 11 3
Note: All the value fields are integers.
Table XYZ:
group condition value ID
================================
B = 50 10
C > 30 11
Note:
Column B and condition are varchar strings and value, ID columns are integers.
Using MySQL 5.5
I'm joining both the tables on the basis of column, ID.
There are many values for group in table XYZ and those values are columns in table ABC.
So, my questions are:
Once the ID matches, I will get the value of group as B in the above case from table XYZ. Then using the value, how should I get the value of B from table ABC?
I want to do something like :
select A.(X.group) from ABC A join XYZ X on A.ID = X.ID;
Once I'm able to get the value of column B using step 1 (as above), I need to form an expression like (group condition value) e.g. in able case B = 50 and replace the value of B from table ABC in the expression. The formed expression needs to be evaluated and if true, need to return the ABC_id from table ABC. How can I evaluate the expression?
I'm doing something like this:
select A.ABC_ID from XYZ X join ABC A on A.ID = X.ID where X.(A.group) A.condition A.value;
I've tried using PREPARE'ing and EXECUTE'ing but the join of ABC and XYZ would give multiple rows as a set. And it does not work.
SELECT X.group, X.condition, X.value
from XYZ X join ABC A
on X.ID = A.ID
into #colname, #condition, #value;
SET #qry = CONCAT('SELECT * from ABC WHERE ',#colname,#condition,#value);
PREPARE stmt FROM #qry;
EXECUTE stmt;
Thanks.
I created a sqlfiddle of this here: http://sqlfiddle.com/#!2/15e205/5
Note that I changed the names of group and condition because those are reserved words, and really aren't good choices for column names.
I'm using CONCAT() to build the query based on the values retrieved in XYZ.
SELECT grp, cndition, value
from XYZ
where id = 10
into #colname, #condition, #value;
SET #qry = CONCAT('SELECT * from abc WHERE ',#colname,#condition,#value);
PREPARE stmt FROM #qry;
EXECUTE stmt;

mysql select update

Got this:
Table a
ID RelatedBs
1 NULL
2 NULL
Table b
AID ID
1 1
1 2
1 3
2 4
2 5
2 6
Need Table a to have a comma separated list as given in table b. And then table b will become obsolete:
Table a
ID RelatedBs
1 1,2,3
2 4,5,6
This does not rund through all records, but just ad one 'b' to 'table a'
UPDATE a, b
SET relatedbs = CONCAT(relatedbs,',',b.id)
WHERE a.id = b.aid
UPDATE: Thanks, 3 correct answers (marked oldest as answer)! GROUP_CONCAT is the one to use. No need to insert commas between the ids using relatedids = CONCAT(relatedids,',',next_id) that is done automatic by GROUP_CONCAT.
You'll have to use the mysql group_concat function in order to achieve this: http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_group-concat
Look into GROUP_CONCAT(expr)
mysql> SELECT student_name,
-> GROUP_CONCAT(DISTINCT test_score
-> ORDER BY test_score DESC SEPARATOR " ")
-> FROM student
-> GROUP BY student_name;
You can't do that in standard SQL. You could write a stored procedure to do that. I had a similar problem, but I was using PostgreSQL so I was able to resolve it by writing a custom aggregate function so that you can do queries like
select aid, concat(id)
from b group by
aid
Update: MySQL has a group_concat aggregate function so you can do something like
SELECT id,GROUP_CONCAT(client_id) FROM services WHERE id = 3 GROUP BY id
as outlined here.