TSQL: Join columns, but rows in one column have multiple values - sql-server-2008

I have a query where I have to join two tables. Lets say T1 and T2.
T1 has a column with an Id. And every row has only one Id value. However, the second table is where i'm struggling. in T2 there is a column with the Id's but it can be possible that one row has multiple of those Id's.
So as example T1.col1 has this value: 737382. But T2.col2 can have this entries in one row: 737382;239112;2838210;9923834;2388342;...
I know that this structure violates the 1NF and stuff. But i cannot change anything in the data or structure of the data.
now what i want to do is to join this two tables. Please let me know if i'm missing to tell any relevant information in order to answer my question. its late and my brain is exhausted ~.~

try
select tab2.* -- whatever
from t1 tab1
inner join t2 tab2 on ( ';'||tab2.col2||';' like '%;'||tab1.col1||';%' )
;
the extra affixed ; characters serve to avoid disjunctions in the join condition.

You could use regular expressions in your join, your regular expression can check for your T1.col1 in T2.col2. The regular expression should check for the value from the begining of the string (i.e. T2.col2) or being preceeded by ';' and always followed by ';'

Have you tried something like:
select
a.column1,
a.column2,
b.column1,
b.column2
from table a
inner join table b on a.column1 = b.column1

Since one T2.Col2 can hold n entries, you need to parse them to rows using a table-valued function and then using CROSS APPLY
BEWARE if the T2 is big this solution will hang for quite long time
something like this:
;WITH IDSplitted AS
(
SELECT *
FROM T2
CROSS APPLY dbo.[StringSplit](col2, ';')
)
SELECT * -- or whatever columns you need
FROM T1
INNER JOIN IDSplitted ON IDSplitted.val = t1.col1
having StringSplit:
CREATE FUNCTION [dbo].[StringSplit]
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END

After using the solution of collapstar (which was correct) i encountered performance issues. so what i did is to create a mapping table so that when i have to run the query again, i dont have to wait so long. so i have a scheduled job which does the join and writes the output into a mapping table over night

Related

Ambiguous column name in update's where statement

I have one result table and two source tables. One source have same column name for key as result table, and second one have slightly different (same values, just different column name).
So if i use code as:
--Works
Update new_table set new_column_1 = source_column_1
from new_table t0
Left join source_table_1 t1
On t0.key_column = t1.id_column
Where key_column = t0.key_column
--Don't
Update new_table set new_column_2 = source_column_2
from new_table t0
Left join source_table_2 t1
On t0.key_column = t1.key_column
Where key_column = t0.key_column
2nd example would give me "Ambiguous column name" error in Where statement. (1st key_column should belong to update destination, 2nd to update source)
I manage around that problem by wrapping source for update into subquery which would rename key column into something else, but i'm curious why that happens in a first place. How come that key names for join make effect on where clause?
You must use the alias since both table have a column named key_column.
This query should work:
Update t0 set new_column_2 = t1.source_column_2 -- or t0.source_column_2?
from new_table t0
Left join source_table_2 t1
On t0.key_column = t1.key_column
Where t1.key_column = t0.key_column
The alias will replace the table name between update and set.
But the where clause does not seem necessary. It is identical to the ON clause. A INNER JOIN would be more appropriate here.

Append text from one table to another table

Got a small question in which I can't wrap my head around..
I have 2 tables, 1 table where there is a Youtube Link i need to append to another column in table 2. But not every record has a Youtube link, which means I need to check if in both tables the names are the same (WHERE name1 = name2?) But how can I append text to already existing text in table 2? Something like this?
UPDATE table2 SET text2 = (text2 + '/n' + table1.text1) WHERE name1 = name2?
If someone could help me, would be awesome!
EDIT:
So I fumbled a bit with the queries:
SELECT
'Kleding'.'Naam',
'jos_virtuemart_products_nl_nl'.'product_name',
CONCAT_WS('/n', 'jos_virtuemart_products_nl_nl'.'product_desc', 'Kleding'.'Youtube_link')
FROM
'jos_virtuemart_products_nl_nl' as 't2'
INNER JOIN 'Kleding' as 't1'
ON 't2'.'product_name' = 't1'.'Naam';
But this query is incorrect for some strange reason. I cannot find why it is incorrect.
You can use INNER JOIN in UPDATE:
UPDATE
table2 as t2 INNER JOIN table1 as t1 ON t2.name = t1.name
SET
t2.text2 = CONCAT_WS('/n', t2.text2, t1.text1);
P.S.: Maybe by '/n' you mean '\n' ?
P.P.S: Before any major data change operation I suggest you to backup your database.
UPDv1:
Test your end resultset with SELECT:
SELECT
`t1`.`name` as `t1-name`,
`t2`.`name` as `t2-name`,
CONCAT_WS('/n', `t2`.`text2`, `t1`.`text1`) as `changes`
-- ^ here you will see what comes up
FROM
`table2` as `t2` INNER JOIN `table1` as `t1` ON `t2`.`name` = `t1`.`name`
-- WHERE possibly more conditions to meet your requirements
If you manage te get proper resultset with additional WHERE conditions, then move them to the UPDATE statement.

add multiple (decimals) values from two tables to update a third

This SHOULD be trivial but I am going round in circles, perhaps someone can help.
I have two tables (T1, T2) from which I wish to extract a number of values in each row and update the contents of a third table (T3) iff (if and only if) two UQ, NN fields in T1, T2 match, in which case I want to add some of the values in the corresponding rows from T1, T2 together and put them in T3 and copy some other values over to T3.
The fields to be summed are all declared DECIMAL.
Simplified and in pseudocode (to avoid making too many assumptions):
SELECT T1.a,T1.b,T1.c from T1
SELECT T2.d, T2.e from T2
UPDATE T3.col1=a, T3.col2=b, T3.col3 = (value of(T2.c) + value of(T2.e)) iff T1.a = T2.d
A variety of attempts have failed to work.
I am running MySQL Workbench 5.2.37 on Ubuntu 12.10
Example from comment below:
UPDATE Test_join as T3
SELECT GZHident, Magnitude_1 from GZHTableExtended3 as T1
SELECT AHZid, DM from AHZDMConversionTable as T2 JOIN T2,T1
ON T1.GZHident = T2.AHZid
SET T3.AHZid = T1.GZHident
SET T3.DM = T2.DM
SET T3.Abs_Magnitude_1 = T1.Magnitude_1 + T2.DM;
MySQL supports multi-table UPDATE using JOIN syntax, just like SELECT. This isn't standard ANSI SQL, but it's very useful for these types of situations.
Example:
UPDATE T1
JOIN T2 ON T1.a = T2.d
JOIN T3 ON ...???...
SET T3.col1=T1.a,
T3.col2=T1.b,
T3.col3 = T2.c + T2.e;
You don't have enough information in your question for me to guess how to join to T3.
Thanks for posting an example UPDATE query. But it's still not clear how you want to update T3. The sample query you give is not valid SQL code at all. I suggest you read a tutorial on programming in SQL.
You seem be confused between UPDATE and INSERT? Keep in mind:
INSERT adds new rows.
UPDATE changes values in existing rows.
I'm guessing from your wording, but perhaps you want to add new rows. You can do that in this way:
INSERT INTO Test_join (AHZid, DM, Abs_Magnitude)
SELECT T1.GZHident, T2.DM, T1.Magnitude_1 + T2.DM
FROM GZHTableExtended3 as T1
JOIN AHZDMConversionTable as T2 JOIN T2
ON T1.GZHident = T2.AHZid;

Loop through column and update it with MySQL?

I want to loop through some records and update them with an ad hoc query in MySql. I have a name field, so I just want to loop though all of them and append a counter to each name, so it will be name1, name2, name3. Most examples I see use stored procs, but I don't need a stored proc.
As a stepping stone on your way to developing an UPDATE statement, first generate a SELECT statement that generates the new name values to your liking. For example:
SELECT t.id
, t.name
, CONCAT(t.name,s.seq) AS new_name
FROM ( SELECT #i := #i + 1 AS seq
, m.id
FROM mytable m
JOIN (SELECT #i := 0) i
ORDER BY m.id
) s
JOIN mytable t
ON t.id = s.id
ORDER BY t.id
To unpack that a bit... the #i is a MySQL user variable. We use an inline view (aliased as i) to initialize #i to a value of 0. This inline view is joined to the table to be updated, and each row gets assigned an ascending integer value (aliased as seq) 1,2,3...
We also retrieve a primary (or unique) key value, so that we can match each of the rows from the inline view (one-to-one) to the table to be updated.
It's important that you understand how that statement is working, before you attempt writing an UPDATE statement following the same pattern.
We can now use that SELECT statement as an inline view in an UPDATE statement, for example:
UPDATE ( SELECT t.id
, t.name
, CONCAT(t.name,s.seq) AS new_name
FROM ( SELECT #i := #i + 1 AS seq
, m.id
FROM mytable m
JOIN (SELECT #i := 0) i
ORDER BY m.id
) s
JOIN mytable t
ON t.id = s.id
ORDER BY t.id
) r
JOIN mytable u
ON u.id = r.id
SET u.name = r.new_name
SQL Fiddle demonstration here:
http://sqlfiddle.com/#!2/a8796/1
I had to extrapolate, and provide a table name (mytable) and a column name for a primary key column (id).
In the SQL Fiddle, there's a second table, named prodtable which is identical to mytable. SQL Fiddle only allows SELECT in the query pane, so in order to demonstrate BOTH the SELECT and the UPDATE, I needed two identical tables.
CAVEAT: be VERY careful in using MySQL user variables. I typically use them only in SELECT statements, where the behavior is very consistent, with careful coding. With DML statements, it gets more dicey. The behavior may not be as consistent in DML, the "trick" is to use a SELECT statement as an inline view. MySQL (v5.1 and v5.5) will process the query for the inline view and materialize the resultset as a temporary MyISAM table.
I have successfully used this technique to assign values in an UPDATE statement. But (IMPORTANT NOTE) the MySQL documentation does NOT specify that this usage or MySQL user variables is supported, or guaranteed, or that this behavior will not change in a future release.
Have the names stored in a table. Do a join against the names and update in the second table you want to.
Thanks

Issue with the TOP 1 query

Is it possible to achieve next thing without using views, but just one single query? I have two tables:
TableA->TanbleB (1-many) ON TableA.Id = TableB.TableAId
I need to update one field in Table A (TableA.Field1) for records in TableA that satisfy condition on one field in tableA (WHERE TableA.Field2=SomeValue)
.
TableA.Field1 will be updated from TableB with value that is last inserted (last inserted value in related records to TableA).
I will put an example:
UPDATE TableA a SET Field1 = (SELECT TOP 1 b.Feild1 * b.Field2 FROM TableB b WHERE b.TableAId = a.id) WHERE field2 = 1
I know Above example doesn't work, but I have many ways tried using INNER JOIN and failed. I had an idea to use something like this:
UPDATE TableA INNDER JOIN ( SELECT ... FROM TABLE B) ON TABLEA.Id= TableB.TableAId SET ....
But the 2ns query should return 1 record for each DISTINCT TableAId, but only the last inserted.
I hope I am making some sense here.
Thanks in advance.
Here is some SQL that will do what you want
UPDATE T1 INNER JOIN T2 ON T1.ID = T2.T1ID SET T1.F2 = [T2].[F2]*[T2].[F3] WHERE (((T1.F1)="ABC") AND ((T2.ID)=DMax("[ID]","[T2]","[T1ID]=" & [T1].[ID])));
This predicated on T1.ID being the primary key for T1 and T2.T1ID being a index field in T2
One of the flaws in Access is that you can't run an "UPDATE" query based on a "SELECT" query, it will usually give the error:
Operation must use an updateable query
The only way around is as you say to create a view of the "SELECT" query and then inner join this on your table, Access is then working with a static recordset and can handle the "UPDATE" query ok
Alternatively you could write a VBA procedure to step through line by line with the Recordset.
Best of luck : )
UPDATE:
SELECT b.TableAId, b.Feild1 * b.Field2 INTO tblView FROM TableB As b WHERE b.field2 = 1