SQL Query Joining Update and Select in a single statement - sql

Need a SQL query to perform UPDATE and SELECT in a single statement.
I have two tables.
First I need to filter the second table
After filtering the second table, I need to update first table from the resultant filtered second table.
update statement followed by Select statement

You haven't told us what your database is, but this will work for SQL Server.
update FirstTable
set f.FirstName = case when f.FirstName = s.MiddleName then s.FN else f.FirstName end,
f.LastName = case when f.LastName = s.MiddleName then s.FN else f.LastName end
from FirstTable f
inner join SecondTable s on f.FirstName = s.MiddleName or f.LastName = s.MiddleName
where s.FN <> 'aaa' -- Missed first time
I don't think it will work exactly as laid out for other RDBMs'.
EDIT: Added the where to the SQL, and the explanation that follows:
The query is of the form:
update table1
set col1 = x.colN
from table1 t
inner join table2 x on some condition
This is in two parts the update + set lines, and the from query which is a complete select statement without the actual select column list. Think of the second as being prefixed by 'select *'.
The second part (the guts of a select statement) must use the same table as the update part - FirstTable in your problem and table1 in the skeleton above. Since it selects rows from the same table as the update it knows which rows you want to update, and makes the columns in the select part available to the update part. In the skeleton, I can then set col1 from table1 to colN from table2.
Your problem is a bit more complicated as I don't necessarily want to change FirstName or LastName. To take FirstName, I want to change FirstName to FN when FirstName = MiddleName. To do this I use a case as part of the set:
set f.FirstName = case when f.FirstName = s.MiddleName then s.FN else f.FirstName end
This says the when FirstName = MiddleName then set FirstName o FN, else set it to FirstName (in other words don't change it).
Repeat for LastName.
Hope that helps.

Related

OracleSQL: How do I add a specific AND is not null OR is not null to my query

Backstory:
I have three tables I'm working with. A directory table (directory), an general attribute table (attribute1table) and a specific attribute table (attribute2table). The general attribute tables hold attribute names (ex. Last Name) under attribute id's (attrid = 2). The specific attribute table holds specific data for these attributes (ex. Doe).
I needed to transpose rows to columns. I had tried using pivot, and max(decode) before but all options gave me the wrong string value- so I used a sub select within the select statement. This worked well- it did transpose the rows into columns but gave me a bunch of null values. See query at the bottom for steps.
Then I added in a general 'stringval IS NOT NULL' to eliminate any of the other attribute1table.attrid's (ex. 4, 5, 6). This worked.
This is the output I was getting at this point. The ? are null values.
Name DataID LastName FirstName
File10 1290 ? Jane
File10 1290 Doe ?
Then I wanted to add in a specification. Essentially to include the values where LastName is not null OR FirstName is not null. I found that someone had recommended doing this in a previous question albeit their situation was different. Eliminating specific null values in sql select
I was able to include one statement or the other but could not add in both. Instead of getting an error I just got a horrifically long run time with no foreseeable result (note that I am using software which lets you input oracle queries within the interface to query the database). It works if I run the query up until the ** (see code) but as soon as I add in the OR condition, it doesn't work anymore. I think this is because I have multiple WHERE conditions. In all cases I want the directory ID and general stringval conditions to apply but I want to have a third condition where either lastname is not null or first name is not null. I'm not sure if I'm missing something obvious- please help?
Here is my current query:
SELECT directory.name, directory.dataid,
(SELECT max(stringval) FROM attribute2table WHERE attribute1table.attrid = 2) as LastName,
(SELECT max(stringval) FROM attribute2table WHERE attribute1table.attrid = 3) as FirstName
FROM attribute2table
JOIN directory ON directory.dataid = attribute2table.id
JOIN attribute1table ON attribute1table.id = directory.dataid
WHERE directory.dataid = 1290
AND stringval IS NOT NULL
AND (SELECT max(valstr) FROM attribute1table WHERE attribute1table.attrid = 2) IS NOT NULL
**OR (SELECT max(valstr) FROM attribute1table WHERE attribute1table.attrid = 3) IS NOT NULL**
Basically I just need to get rid of the null values and want my table to look like....
Name DataID LastName FirstName
File10 1290 Doe Jane
This appears to be a parenthesization issue. If I understand the issue, you need to put the two IS NOT NULL conditions in parentheses:
SELECT directory.name,
directory.dataid,
m2.LastName,
m3.FirstName
FROM attribute2table
INNER JOIN directory
ON directory.dataid = attribute2table.id
INNER JOIN attribute1table
ON attribute1table.id = directory.dataid
LEFT OUTER JOIN (SELECT max(valstr) AS LASTNAME
FROM attribute1table
WHERE attribute1table.attrid = 2) m2
ON 1 = 1
LEFT OUTER JOIN (SELECT max(valstr) AS FIRSTNAME
FROM attribute1table
WHERE attribute1table.attrid = 3) m3
ON 1 = 1
WHERE directory.dataid = 1290 AND
stringval IS NOT NULL AND
(m2.LASTNAME IS NOT NULL OR
m3.FIRSTNAME IS NOT NULL)
I also rewrote the query using joins instead of subselects as I think it's a bit clearer.
Note also that in the M2 and M3 joins I used LEFT OUTER with a condition of 1 = 1 rather than using CROSS JOIN, because I've noticed that CROSS JOIN acts like an INNER JOIN if the query being cross-joined returns no rows - that is, it causes the entire SELECT to return no data. dbfiddle demonstrating this situation here
I'm pretty sure you just need conditional aggregation:
SELECT d.name, d.dataid,
MAX(CASE WHEN a1.attrid = 2 THEN a2.stringval END) as LastName,
MAX(CASE WHEN a1.attrid = 3 THEN a2.stringval END) as FirstName
FROM directory d JOIN
attribute2table a2
ON a2.id = d.dataid JOIN
attribute1table a1
ON a1.id = d.dataid
WHERE d.dataid = 1290
GROUP BY d.name, d.dataid

SQL give cells the same value if cell in another column is partly similar

I'm pretty new to SQL. And I'm trying the following.
I have the following table1 with Columns group and description.
Table1 Example
I want to check if the cell values are partly the same. Like in this case Group101 and Group101D1. Now I want to take the description from the value with D1 and put it in the description column of the one without D1.
And do this for all the cells where the Group value is partly the same.
SELECT [Group]
,[Description]
FROM [Table1]
update [Table1]
set Description = (Select [Description] from [Table1] where [Group] like '%Group101D1%')
where Address like '%Group1%'
This is how far I came. I can make it work for one but have to split it up and make it work for all of them.
Try this query:
WITH cte AS (
SELECT t1.Description AS desc_dest, t2.Description AS desc_src
FROM yourTable t1
INNER JOIN yourTable t2
ON t2.[Group] LIKE t1.[Group] + '[A-Z]%' AND LEN(t1.[Group]) <= 9
)
UPDATE cte
SET desc_dest = desc_src;
Demo
If you simply want to get consistency between all descriptions that only differ in the last two characters, the following should to the trick:
WITH CTE AS
(
SELECT
a.Description
, b.Description AS Descriptionb
FROM
TABLE1 a
JOIN [Table1] b ON LEFT(a.[Group], LEN(a.[Group]) - 2) = b.[Group]
)
update CTE
set Description = Descriptionb
The self-join establishes that we look for all descriptions where the last 2 characters from set a (Group101D1) are removed so we end up with Group101. We then look for a match in set b. If we find it, we set the values of b to match the values in a.
This means the description of Group101D1 will be set to Group101, as would Group101XX. It also should allow for handling of Group1010404D1 to change Group1010404.
Edit: Switched the UPDATE around. I read things wrong and it would have updated the wrong values.
Edit2: As Tim said, my join was wrong. The above should work, though.
My solution is the following:
SELECT [Table1].[Description]
, [Table2].[Description]
,[Table1].[Group]
,[Table2].[Group]
--update [Table1] set [Table1].[Description] = [Table2].[Description]
FROM [BVCIOList_DOW].[dbo].[Table] Table1
INNER JOIN [BVCIOList_DOW].[dbo].[Table] Table2
ON [Table2].[Group] = [Table1].[Group] + 'D1'

SQL Server 2008 - Union of 4 queries and ordering by relevance

I'm facing an issue that my query cannot order by relevance since I declared 'column' MATCH and try to ordering by it.
I'm trying to create a stored procedure using UNION.
This query has some rules that I need to follow since I need to bring 3 related articles. Each rule has a query that I tried to unite them.
Let me explain those rules:
I need to search and match and article that has the same TAG related to it inside the same project (as CampanhaId)
I need to search and match the same TAG without be inside the same project, but public articles
Recent articles at the same project
Recent public article
I need to follow these rules in priory and search for first three articles passing by then.
So, if first rule hasn't at least 3 articles, the second rule will try to fill it. The third and fourth rules follow the same way.
I tried to create a query like this:
CREATE PROCEDURE [dbo].[SP_GetNoticiaRelacionada]
(#Tag VARCHAR(50), #ExtranetId INT, #CampanhaAreaId INT, #NoticiaId INT)
AS
BEGIN
SELECT TOP 3 *
FROM
(SELECT DISTINCT
ArtigoId, CategoriaId, Titulo, Conteudo,
Subtitulo, Categoria, FotoCompacta, QtdResposta,
0 AS MATCH, DataAlteracao
FROM
(SELECT
A.ArtigoId, A.CategoriaId, A.Titulo, A.Conteudo,
A.Subtitulo, C.Nome AS Categoria,
A.ImgAlt AS FotoCompacta,
(SELECT COUNT(*) FROM Comentario C
WHERE C.GenericAreaId = A.ArtigoId) AS QtdResposta,
1 AS MATCH, A.DataAlteracao
FROM
Artigo A
JOIN
ArtigoCategoria C ON A.CategoriaId = C.CategoriaId
WHERE
A.Apagado = 0
AND A.TAG COLLATE Latin1_General_CI_AI LIKE '%' + #Tag + '%'
AND A.CampanhaAreaId = #CampanhaAreaId
AND A.ArtigoId <> #NoticiaId
UNION
SELECT A.ArtigoId
,A.CategoriaId
,A.Titulo
,A.Conteudo
,A.Subtitulo
,C.Nome AS Categoria
,A.ImgAlt AS FotoCompacta
,(SELECT COUNT(*) FROM Comentario C WHERE C.GenericAreaId = A.ArtigoId) AS QtdResposta
,2 AS MATCH
,A.DataAlteracao
FROM Artigo A
JOIN ArtigoCategoria C ON A.CategoriaId = C.CategoriaId
WHERE A.Apagado = 0 AND A.TAG COLLATE Latin1_General_CI_AI LIKE '%' + #Tag + '%' AND A.CampanhaId = #ExtranetId AND A.ArtigoId <> #NoticiaId
UNION
SELECT A.ArtigoId
,A.CategoriaId
,A.Titulo
,A.Conteudo
,A.Subtitulo
,C.Nome AS Categoria
,A.ImgAlt AS FotoCompacta
,(SELECT COUNT(*) FROM Comentario C WHERE C.GenericAreaId = A.ArtigoId) AS QtdResposta
,3 AS MATCH
,A.DataAlteracao
FROM Artigo A
JOIN ArtigoCategoria C ON A.CategoriaId = C.CategoriaId
WHERE A.Apagado = 0 AND A.CampanhaAreaId = #CampanhaAreaId AND A.ArtigoId <> #NoticiaId
UNION
SELECT A.ArtigoId
,A.CategoriaId
,A.Titulo
,A.Conteudo
,A.Subtitulo
,C.Nome AS Categoria
,A.ImgAlt AS FotoCompacta
,(SELECT COUNT(*) FROM Comentario C WHERE C.GenericAreaId = A.ArtigoId) AS QtdResposta
,4 AS MATCH
,A.DataAlteracao
FROM Artigo A
JOIN ArtigoCategoria C ON A.CategoriaId = C.CategoriaId
WHERE A.Apagado = 0 AND A.CampanhaId = #ExtranetId AND A.ArtigoId <> #NoticiaId
) AS T
GROUP BY
ArtigoId
,CategoriaId
,Titulo
,Conteudo
,Subtitulo
,Categoria
,FotoCompacta
,QtdResposta
,MATCH
,DataAlteracao) AS T2
ORDER BY T2.MATCH ASC, T2.DataAlteracao DESC
END
So, the first query returns only Articles in the same TAG and Project.
The second one, returns all Articles with matching the same TAG.
The third one, matches all Article in the same Project.
The last one matches all Article published.
My real problem, I guess, all the results don't respect that order.
If I have two articles with the same TAG, this should bring first as related articles, but somehow this brings first any article that I updated recently and should not be the first one in the list.
When I tried to execute this procedure, SQL Server always returns the column Match with a value of 0.
I think the problem is inside this Match column that I cannot order by it.
If someone needs more information, please advise me. I'll be appreciate any help.
I don't have any further actions I need to take.
You are doing "SELECT 0 AS MATCH" in your outer query, which means it is over-writing any values in your inner query.
In other words, to expose the issue, your code could be simplified to this:
SELECT 0 AS Match
FROM (
SELECT 1 AS Match
UNION
SELECT 2 AS Match
UNION
SELECT 3 AS Match
UNION
SELECT 4 AS Match
)
ORDER BY Match
Since you are using Match 1-4 in the inner query, but then stating "SELECT 0 AS Match" in the outer query that selects from the inner query, all the rows are going to have 0 for Match.
Instead of getting "0 AS Match" in the outer query, you should just get Match from the inner query.

SQL update statement is updating

I'm confounded by an Update statement that seems easy on the surface. I need to update the values in 4 columns in Table1 with values from Table2 based on matching two separate columns from each table. Here is the general syntax:
Update Table1
set field1 = [table2 field1]
, field2 = [table2 field2]
, field3 = [table2 field3]
, field4 = [table2 field4]
--select *
from Table2 p inner join Table1 c
on p.matchfield1 = c.matchfield1 and p.matchfield2 = c.matchfield2
where c.database_id = 1 --qualifier since join clause may not provide unique result set
and p.matchfield1 = 123456 --to test the statement on a single record first
When I run the clause as a select statement I get a single record as expected. However, when I run the update clause using the same 'from' language I find that the query updates EVERY row in Table1 with the values from Table2 where matchfield1 = 123456.
I'm wondering if I need to further clarify my where clause to qualify based on the matchfield2 value (eamil address in my example) so I'd add to the end of the statement 'and p.matchfield2 = 'email#address.com''.
I suspect I'm a very small tweak away from making this work but in the meantime it's making me half crazy. Thanks for any advice!
Instead of doing a JOIN on the same table, try this (assuming you are using SQL Server)...
UPDATE Table1
set field1 = [table2 field1]
, field2 = [table2 field2]
, field3 = [table2 field3]
, field4 = [table2 field4]
FROM Table2 p
WHERE database_id = 1
AND p.matchfield1 = 123456
AND p.matchfield1 = matchfield1 AND p.matchfield2 = matchfield2
You could also modify your query to tighten the filtering by adding AND c.matchfield1 = matchfield1 AND c.matchfield2 = matchfield2 to the WHERE clause.

Copy values from one table to another in SQL

I have 2 tables. I need to update all rows of table 1 with the values in specific columns from table 2. They have the same structure.
UPDATE #TempTable
SET [MyColumn] =
(
SELECT [MyColumn]
FROM
[udf_AggregateIDs] (#YearId) AS [af]
INNER JOIN [MyForm] ON
(
[af].[FormID] = [MyForm].[FormID] AND
[af].[FormID] = #MyFormId
)
WHERE [Description] = [MyForm].[Description]
)
I get an error saying Subquery returned more than 1 value. I only added the where clause in because i thought sql is struggling to match the rows, but both tables have the same rows.
It should return multiple values because i'm trying to copy across all rows for MyColumn from the one table to the other.
Ideas?
is Description unique ?
select [Description], count(*) from [MyForm] group by [Description] having count(*)>1
You don't need a sub query..just join the tables..
same type of question has been answered here. Hope it helps.
Have to guess here because your query isn't self-documenting. Does MyColumn come from the function? Does #TempTable have a description column? Who knows, because you didn't prefix them with an alias? Try this. You may have to adjust since you know your schema and we don't.
UPDATE t
SET [MyColumn] = func.MyColumn -- have to guess here
FROM dbo.[udf_AggregateIDs] (#YearId) AS func
INNER JOIN dbo.MyForm AS f
ON func.FormID = f.FormID
INNER JOIN #TempTable AS t
ON t.Description = f.Description -- guessing here also
WHERE f.FormID = #MyFormID;