update one table from another selected table - sql-server

I select one column from a table and generated the second column by select case:
(select Id , case
when education=0 then '0::ALL'
when education=1 then '1::HIGH_SCHOOL'
when education=2 then '2::UNDERGRAD'
when education=3 then '3::ALUM'
when education=4 then '4::HIGH_SCHOOL_GRAD'
when education=5 then '5::SOME_COLLEGE'
when education=6 then '6::ASSOCIATE_DEGREE'
when education=7 then '7::IN_GRAD_SCHOOL'
when education=8 then '8::SOME_GRAD_SCHOOL'
when education=9 then '9::MASTER_DEGREE'
when education=10 then '10::PROFESSIONAL_DEGREE'
when education=11 then '11::DOCTORATE_DEGREE'
when education=12 then '12::UNSPECIFIED'
end as myeducation
from ids_table where Id = '4fcc-a519-15db04651b91')
assuming it returns:
------------------------------------------------
| Id myeducation |
| 4fcc-a519-15db04651b91, 9::MASTER_DEGREE |
------------------------------------------------
in the same table (ids_table), I have an empty column is called: allEducations
I want to set allEducations = myeducation where id (of the table above that I "created") is equal to the id of the table (ids_table)
before:
ids_table:
----------------------------------------------
| Id allEducation |
| 4fcc-a519-15db04651b91, |
------------------------------------------------
after:
----------------------------------------------
| Id allEducation |
| 4fcc-a519-15db04651b91, 9::MASTER_DEGREE |
------------------------------------------------
I tried to do something like:
`;WITH b AS (THE SQL QUERY ABOVE) update ids_table c set c.allEducations = b.myeducation where c.id = b.id'
any help appreciated!

This should be enough:
begin tran updateEducation
update ids_table set allEducations =
case
when education=0 then '0::ALL'
when education=1 then '1::HIGH_SCHOOL'
when education=2 then '2::UNDERGRAD'
when education=3 then '3::ALUM'
when education=4 then '4::HIGH_SCHOOL_GRAD'
when education=5 then '5::SOME_COLLEGE'
when education=6 then '6::ASSOCIATE_DEGREE'
when education=7 then '7::IN_GRAD_SCHOOL'
when education=8 then '8::SOME_GRAD_SCHOOL'
when education=9 then '9::MASTER_DEGREE'
when education=10 then '10::PROFESSIONAL_DEGREE'
when education=11 then '11::DOCTORATE_DEGREE'
when education=12 then '12::UNSPECIFIED'
end
---- if it is not good
-- rollback
---- if it is good
-- commit

Related

How to add lots of values to a Postgres column with a where statement

I have a table with 1000 rows and have added a new column but now i need to add the data to it. Below is an example of my table.
location | name | display_name
-----------------+--------+-------
liverpool | Dan |
london | Louise |
stoke-on-trent | Amel |
itchen-hampshire| Mark |
I then have a csv that looks like this that has the extra data
location,name,display_name
Liverpool,Dan,Liverpool
London,Louise,London
stoke-on-trent,Amel,Stoke on Trent
itchen-hampshire,Mark,itchen (hampshire)
i know how to update a single row but not sure for the 1000 rows of data i have?
updating single row
UPDATE info_table
SET display_name = 'Itchen (Hampshire)'
WHERE id = 'itchen-hampshire';
You should first load that CSV data into another table and then do an update join on the first table:
UPDATE yourTable t1
SET display_name = t2.display_name
FROM csvTable t2
WHERE t2.location = t1.location;
If you only want to update display names which are null and have no value, then use:
WHERE t2.location = t1.location AND display_name IS NULL;
Updating more than one columns you can use this genralized query
update test as t set
column_a = c.column_a
from (values
('123', 1),
('345', 2)
) as c(column_b, column_a)
where c.column_b = t.column_b;

Execute a stored procedure with multiple IDs

I have a PersonInformation table that contains the information below:
| PersonID | Name | Status
+----------+------+------------
| 1234 | John | Active
| 5678 | Mary | Inactive
| 1090 | Tery | Active
| 1554 | Cary | Inactive
I also have a stored procedure called SpStats that does some calculations using PersonID and returns the results of stat_a, stat_b.
I need to execute the SpStats stored procedure for each active person in the PersonInformation table using their PersonID.
It is basically
SELECT PersonId, Name, Status, {Exec SpStats for related PersonID}
FROM PersonInformation
WHERE status = 'Active'
The expected result is this:
| PersonID | Name | Status | stat_a | stat_b |
+----------+------+--------+--------+--------+
| 1234 | John | Active | 25 | 45 |
| 1090 | Tery | Active | 10 | 67 |
If you can create a user defined table function that will do what your stored procedure does, you could use cross apply to get it's results for each row in the select.
Assuming it accepts the PersonID value, it can be done like this:
SELECT PersonId, [Name], [Status], stat_a, stat_b
FROM PersonInformation
CROSS APPLY dbo.UDFStats(PersonID) as stats
WHERE [Status] = 'Active'
Note that CROSS APPLY will not work with stored procedures.
Create your own custom type like so
--Table Valued Parameter Type used for passing lists of Int IDs.
CREATE TYPE [MySchema].[UniqueIDListType] AS TABLE
(
[ID] Int NOT NULL
);
Then in your stored proc set the input parameter to your type
CREATE PROCEDURE [MySchema].[MyProc]
#MyIDList [MySchema].[UniqueIDListType] READONLY
AS
And there you go, you can use your parameter like a table in your select
WHERE EXISTS (SELECT * FROM #MyIDList WHERE ID = PersonID);
You will get a full result set for each id then. Pass it in to the proc like you would any other parameter, you'll just need to format the ids into this single collection. And the bonus is you can reuse your new table valued parameter everywhere you need to do this again. Reusability!! Yay!
If you can't do this, then I'm assuming you can't with your requirements or permissions make a function either, I would then just loop through programmatically and make a table variable in your sql, call the proc for each person, insert that result in your table, and join that on your final select result set when you're all done.
If you have a table
CREATE TABLE Country (
CountryId UNIQUEIDENTIFIER
,Abbreviation VARCHAR(10)
,Name VARCHAR(100)
)
And a User Defined Function
CREATE FUNCTION udfConcatenate(#value1 VARCHAR(4000), #value2 VARCHAR(4000))
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN CONCAT(#value1, #value2)
/* HERE YOU CAN EXECUTE YOU SOTRED PROCEDURE AND RETURN THE RESULT */
END
You can execute
select c.CountryId, dbo.udfConcatenate(c.Abbreviation, c.Name) from country AS C
The User Defined Function can be applied for each row.
So, the User Defined Function execute you Sotred Procedure and return the result.
And, you use the User Defined Function in you SELECT.
EDIT
WHILE way
DECLARE #abb VARCHAR(10)
DECLARE #name VARCHAR(200)
DECLARE cur1 CURSOR FOR
SELECT Abbreviation, Name FROM Country
OPEN cur1
FETCH NEXT FROM cur1 INTO #abb, #name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT dbo.udfConcatenate(#abb, #name)
/* HERE YOU CAN EXECUTE THE SP AND STORE THE RESULTS IN A TEMP TABLE */
FETCH NEXT FROM cur1 INTO #abb, #name
END
CLOSE cur1
DEALLOCATE cur1
Hope this help you.

Table names in front of field names when using SELECT *

I was wondering if there is a possibility to show full column names when using SELECT * in combination with JOIN.
As an example I have this query (which selects data that gets imported from another application):
SELECT *
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.SomeKey = t2.SomeKey
LEFT JOIN Table3 t3 ON t2.SomeOtherKey = t3.SomeOtherKey
This gives me results like this:
+---------+------+-------+------------+---------+--------------+-----------+------------+--------------+-------+---------------+------------+
| SomeKey | Name | Value | WhateverId | SomeKey | SomeOtherKey | ValueType | CategoryId | SomeOtherKey | Value | ActualValueId | SomeTypeId |
+---------+------+-------+------------+---------+--------------+-----------+------------+--------------+-------+---------------+------------+
| bla | bla | bla | bla | bla | bla | bla | bla | bla | bla | bla | bla |
+---------+------+-------+------------+---------+--------------+-----------+------------+--------------+-------+---------------+------------+
What I'd like to have is the table name in front of each field. The results would be something like this:
+----------------+-------------+-------+------------------+
| Table1.SomeKey | Table1.Name | ..... | Table2.ValueType | .....
+----------------+-------------+-------+------------------+
| bla | bla | ..... | bla | .....
+----------------+-------------+-------+------------------+
I want to do this because the query is already given (without SELECT *) and I now have to find a column in one of the tables with values that match a given identity from an additional table. I know I could analyze each of the tables. However, I'd like to know if there is any simple statement I could add to get the table names in front of the field names.
Well, the answer to that question is NO . You can't alias the columns using the * .
If you want to work around with the columns, specify them your self and alias them as you'd like, in general I try to avoid the use of * to avoid ambiguously columns.
Think about that this way - 2 more minutes of work to avoid a possibilitiy of errors that will take you 100X of it.
You can easily build yourself the SELECT clause using AS for each column. Let's say you have the following tables:
IF OBJECT_ID('dbo.Table01') IS NOT NULL
BEGIN
DROP TABLE Table01;
END;
IF OBJECT_ID('dbo.Table02') IS NOT NULL
BEGIN
DROP TABLE Table02;
END;
CREATE TABLE Table01
(
[ID] INT IDENTITY(1,1)
,[Name] VARCHAR(12)
,[Age] TINYINT
);
CREATE TABLE Table02
(
[ID] INT IDENTITY(1,1)
,[Name] VARCHAR(12)
,[Age] TINYINT
);
and you query is:
SELECT *
FROM Table01
INNER JOIN Table02
ON Table01.[ID] = Table02.[ID];
Just execute the following statement:
SELECT ',' + [source_table] + '.' + [source_column] + ' AS [' + [source_table] + '.' + [source_column] + ']'
FROM sys.dm_exec_describe_first_result_set
(N'SELECT *
FROM Table01
INNER JOIN Table02
ON Table01.[ID] = Table02.[ID]', null,1) ;
You will get this:
Just copy and paste the result to your query (and remove the first comma):
SELECT Table01.ID AS [Table01.ID]
,Table01.Name AS [Table01.Name]
,Table01.Age AS [Table01.Age]
,Table02.ID AS [Table02.ID]
,Table02.Name AS [Table02.Name]
,Table02.Age AS [Table02.Age]
FROM Table01
INNER JOIN Table02
ON Table01.[ID] = Table02.[ID];
and you will get this:
Of course you can play with the output of the function and build the SELECT columns in a way you like (excluding columns, formatting it, etc).

Set-based approach to updating multiple tables, rather than a WHILE loop?

Apparently I'm far too used to procedural programming, and I don't know how to handle this with a set-based approach.
I have several temporary tables in SQL Server, each with thousands of records. Some of them have tens of thousands of records each, but they're all part of a record set. I'm basically loading a bunch of xml data that looks like this:
<root>
<entry>
<id-number>12345678</id-number>
<col1>blah</col1>
<col2>heh</col2>
<more-information>
<col1>werr</col1>
<col2>pop</col2>
<col3>test</col3>
</more-information>
<even-more-information>
<col1>czxn</col1>
<col2>asd</col2>
<col3>yyuy</col3>
<col4>moat</col4>
</even-more-information>
<even-more-information>
<col1>uioi</col1>
<col2>qwe</col2>
<col3>rtyu</col3>
<col4>poiu</col4>
</even-more-information>
</entry>
<entry>
<id-number>12345679</id-number>
<col1>bleh</col1>
<col2>sup</col2>
<more-information>
<col1>rrew</col1>
<col2>top</col2>
<col3>nest</col3>
</more-information>
<more-information>
<col1>234k</col1>
<col2>fftw</col2>
<col3>west</col3>
</more-information>
<even-more-information>
<col1>asdj</col1>
<col2>dsa</col2>
<col3>mnbb</col3>
<col4>boat</col4>
</even-more-information>
</entry>
</root>
Here's a brief display of what the temporary tables look like:
Temporary Table 1 (entry):
+------------+--------+--------+
| UniqueID | col1 | col2 |
+------------+--------+--------+
| 732013 | blah | heh |
| 732014 | bleh | sup |
+------------+--------+--------+
Temporary Table 2 (more-information):
+------------+--------+--------+--------+
| UniqueID | col1 | col2 | col3 |
+------------+--------+--------+--------+
| 732013 | werr | pop | test |
| 732014 | rrew | top | nest |
| 732014 | 234k | ffw | west |
+------------+--------+--------+--------+
Temporary Table 3 (even-more-information):
+------------+--------+--------+--------+--------+
| UniqueID | col1 | col2 | col3 | col4 |
+------------+--------+--------+--------+--------+
| 732013 | czxn | asd | yyuy | moat |
| 732013 | uioi | qwe | rtyu | poiu |
| 732014 | asdj | dsa | mnbb | boat |
+------------+--------+--------+--------+--------+
I am loading this data from an XML file, and have found that this is the only way I can tell which information belongs to which record, so every single temporary table has the following inserted at the top:
T.value('../../id-number[1]', 'VARCHAR(8)') UniqueID,
As you can see, each temporary table has a UniqueID assigned to it's particular record to indicate that it belongs to the main record. I have a large set of items in the database, and I want to update every single column in each non-temporary table using a set-based approach, but it must be restricted by UniqueID.
In tables other than the first one, there is a Foreign_ID based on the PrimaryKey_ID of the main table, and the UniqueID will not be inserted... it's just to help tell what goes where.
Here's the exact logic that I'm trying to figure out:
If id-number currently exists in the main table, update tables based on the PrimaryKey_ID number of the main table, which is the same exact number in every table's Foreign_ID. The foreign-key'd tables will have a totally different number than the id-number -- they are not the same.
If id-number does not exist, insert the record. I have done this part.
However, I'm currently stuck in the mind-set that I have to set temporary variables, such as #IDNumber, and #ForeignID, and then loop through it. Not only am I getting multiple results instead of the current result, but everyone says WHILE shouldn't be used, especially for such a large volume of data.
How do I update these tables using a set-based approach?
Assuming you already have this XML extracted, you could do something similar to:
UPDATE ent
SET ent.col1 = tmp1.col1,
ent.col2 = tmp1.col2
FROM dbo.[Entry] ent
INNER JOIN #TempEntry tmp1
ON tmp1.UniqueID = ent.UniqueID;
UPDATE mi
SET mi.col1 = tmp2.col1,
mi.col2 = tmp2.col2,
mi.col3 = tmp2.col3
FROM dbo.[MoreInformation] mi
INNER JOIN dbo.[Entry] ent -- mapping of Foreign_ID ->UniqueID
ON ent.PrimaryKey_ID = mi.Foreign_ID
INNER JOIN #TempMoreInfo tmp2
ON tmp2.UniqueID = ent.UniqueID
AND tmp2.SomeOtherField = mi.SomeOtherField; -- need 1 more field
UPDATE emi
SET ent.col1 = tmp3.col1,
emi.col2 = tmp3.col2,
emi.col3 = tmp3.col3,
emi.col4 = tmp3.col4
FROM dbo.[EvenMoreInformation] emi
INNER JOIN dbo.[Entry] ent -- mapping of Foreign_ID ->UniqueID
ON ent.PrimaryKey_ID = mi.Foreign_ID
INNER JOIN #TempEvenMoreInfo tmp3
ON tmp3.UniqueID = ent.UniqueID
AND tmp3.SomeOtherField = emi.SomeOtherField; -- need 1 more field
Now, I should point out that if the goal is truly to
update every single column in each non-temporary table
then there is a conceptual issue for any sub-tables that have multiple records. If there is no record in that table that will remain the same outside of the Foreign_ID field (and I guess the PK of that table?), then how do you know which row is which for the update? Sure, you can find the correct Foreign_ID based on the UniqueID mapping already in the non-temporary Entry table, but there needs to be at least one field that is not an IDENTITY (or UNIQUEIDENTIFIER populated via NEWID or NEWSEQUENTIALID) that will be used to find the exact row.
If it is not possible to find a stable, matching field, then you have no choice but to do a wipe-and-replace method instead.
P.S. I used to recommend the MERGE command but have since stopped due to learning of all of the bugs and issues with it. The "nicer" syntax is just not worth the potential problems. For more info, please see Use Caution with SQL Server's MERGE Statement.
you can use MERGE which does upsert ( update and insert ) in a single statement
First merge entries to the main table
For other tables, you can do a join with main table to get foreign id mapping
MERGE Table2 as Dest
USING ( select t2.*, m.primaryKey-Id as foreign_ID
from #tempTable2 t2
join mainTable m
on t2.id-number = m.id-number
) as Source
on Dest.Foreign_ID = m.foreign_ID
WHEN MATCHED
THEN Update SET Dest.COL1 = Source.Col1
WHEN NOT MATCHED then
INSERT (FOREGIN_ID, col1, col2,...)
values ( src.foreign_Id, src.col1, src.col2....)

A trigger to update one column value to equal the pkid of the record

I need to write a trigger that will set the value in column 2 = to the value in column 1 after a record has been created.
This is what I have so far:
create trigger update_docindex2_to_docid
ON dbo.TABLENAME
after insert
AS BEGIN
set DOCINDEX2 = DOCID
END;
I answered my own question one I sat and thought about it long enough....
This seems way to simple. I'm concerned that I'm going break something because I don't have a where condition that would identify the correct record. I want this to update docindex2 to the newly created DOCID after a record is created in the database. The docid is the pkid.
Any ideas/suggestions are appreciated....
Are you looking for something like this?
CREATE TABLE Table1 (docid INT IDENTITY PRIMARY KEY, docindex2 INT);
CREATE TRIGGER tg_mytrigger
ON Table1 AFTER INSERT
AS
UPDATE t
SET t.docindex2 = t.docid
FROM Table1 t JOIN INSERTED i
ON t.docid = i.docid;
INSERT INTO Table1 (docindex2) VALUES(0), (0);
Contents of Table after insert
| DOCID | DOCINDEX2 |
---------------------
| 1 | 1 |
| 2 | 2 |
Here is SQLFiddle demo

Resources