Import/Update data from Excel to SQL Server - sql

I'm converting a database from Excel onto MS SQL server and am basically clueless.
The Excel file has column headings of GroupID, Name, Members, Remarks
The SQL table has the same fields.
When I update the SQL some records are totally new, so need to be appended, others need a column or two updated, while most records need nothing at all. So far I've taken the lazy way out & truncated the file & appended everything back in, but what's the proper way?

Import the file as a separate table and you can do all your updates from there. Depending on your version of SQL server you may be able to use the MERGE statement. It shouldn't take too long to knock up an insert, and an update statement.
Something like this for the update:
UPDATE o
SET name = i.name
FROM originaltablename o
INNER JOIN importedexceltablename i
ON o.GroupID = i.GroupID
WHERE o.name <> i.name
And something like this for the insert:
INSERT INTO originaltablename
SELECT i.*
FROM importedexceltablename i
LEFT JOIN originaltablename o
ON o.GroupID = i.GroupID
WHERE o.GroupID IS NULL
Be careful though, this is just an example to get you going as you haven't given enough information for a proper solution.

Related

SQL update multiple rows with different values where they match a value from a list

So perhaps the title is a little confusing. If you can suggest better wording for that please let me know and i'll update.
Here's the issue. I've got a table with many thousands of rows and i need to update a few thousand of those many to store latest email data.
For example:
OldEmail#1.com => NewEmail#1.com
OldEmail#2.com => NewEmail#2.com
I've got a list of old emails ('OldEmail#1.com','OldEmail#2.com') and a list of the new ('NewEmail#1.com','NewEmail#2.com'). The HOPE was was to sort of do it simply with something like
UPDATE Table
SET Email = ('NewEmail#1.com','NewEmail#2.com')
WHERE Email = ('OldEmail#1.com','OldEmail#2.com')
I hope that makes sense. Any questions just ask. Thanks!
You could use a case expression:
update mytable
set email = case email
when 'OldEmail#1.com' then 'NewEmail#1.com'
when 'OldEmail#2.com' then 'NewEmail#2.com'
end
where email in ('OldEmail#1.com','OldEmail#2.com')
Or better yet, if you have a large list of values, you might create a table to store them (like myref(old_email, new_email)) and join it in your update query, like so:
update t
set t.email = r.new_email
from mytable t
inner join myref r on r.old_email = t.email
The actual syntax for update/join does vary accross databases - the above SQL Server syntax.
With accuracy to the syntax in particular DBMS:
WITH cte AS (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com')
UPDATE table
SET table.email = cte.newvalue
FROM cte
WHERE table.email = cte.oldvalue
or, if CTE is not available,
UPDATE table
SET table.email = cte.newvalue
FROM (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com') cte
WHERE table.email = cte.oldvalue
Consider prepared statement for rows update in large batches.
Basically it works as following :
database complies a query pattern you provide the first time, keep the compiled result for current connection (depends on implementation).
then you updates all the rows, by sending shortened label of the prepared function with different parameters in SQL syntax, instead of sending entire UPDATE statement several times for several updates
the database parse the shortened label of the prepared function , which is linked to the pre-compiled result, then perform the updates.
next time when you perform row updates, the database may still use the pre-compiled result and quickly complete the operations (so the first step above can be skipped).
Here is PostgreSQL example of prepare statement, many of SQL databases (e.g. MariaDB,MySQL, Oracle) also support it.

MS Access update query reversing table placement in query. Why does it work?

I have a question on SQL execution sequence. I'm using nested iif() conditional statements in MS-Access and because character length became too long, I wanted to use an alias in the statement.
I tried this (by accident) and it works and I'm not sure why and if I should actually use it. Below are shortened examples of the original format compared to the second with enough of the statement to get the gist of it (using generic table names).
I want to update the upDateMe table.
Original pre-alias :
UPDATE upDateMe
INNER JOIN linkMe
ON (linkMe.UniqueID = upDateMe.UniqueID)
AND (linkMe.SrcNumber = upDateMe.SrcNumber)
SET upDateMe.ExpiryDate = [linkMe].[ExpiryDate]
, upDateMe.PermitEnd = [linkMe].[PermitEnd]...
Here I've reversed the tables and put in the alias 'bData':
UPDATE linkMe
INNER JOIN upDateMe AS bData
ON (linkMe.UniqueID = bData.UniqueID)
AND (linkMe.SrcNumber = bData.SrcNumber)
SET bData.ExpiryDate = [linkMe].[ExpiryDate]
, bData.PermitEnd = [linkMe].[PermitEnd]...
This second query works!??. I'm not really sure as to why it would. Can someone explain it??
Because the left side of the set statement is ALWAYS what gets updated.
In code (most maybe not all) you set a variable by setting it = to some value on the right side. The LEFT side is always the target.
In this case, you're telling the database to update the join of linkMe and upDateMe (as bData) setting the bData values to the linkme values. You would likely get an error if you tried to update both bdate and linkMe at the same time as engines generally are only able to update 1 table at a time, since no such conflict seems to exist here, bData is updated without issue.

SQL: use computed string as table name, join all rows of select for counting

I have access to a MS SQL Server with MS Access via ODBC and I want to display the table names, their column names and the number of rows per table. The table names are stored in a table named "sys_tables", the column names in "sys_columns". Unfornunately the number of rows per table has to be counted. As I'm not experienced in SQL, my first try is not working:
SELECT t.name, c.name, t.object_id, x.cnt
FROM sys_tables AS t INNER JOIN sys_columns AS c ON t.object_id = c.object_id
LEFT OUTER JOIN (SELECT COUNT(*) AS cnt FROM #t.name AS tbl ON tbl.cnt > 0) AS x
What is the right way to use a computed string in a SELECT as table name? Can I do a sub select that selects all rows without real relation?
You can't use a variable table name in a SQL statement on the server side. You will need to get the table names from SQL Server and then use those to send new queries (one for each table) back to SQL Server. You could also write a stored procedure on the SQL Server to handle this using dynamic SQL (just be sure that you are familiar with injection attacks whenever you are using dynamic SQL).
SQL Server stores some row count information in sysindexes (not to be confused with sys.indexes) in the rowcnt column. This information is not always 100% accurate though.
Finally, you can use the undocumented stored procedure sp_MSForEachTable like so:
EXEC sp_msForEachTable
'SELECT PARSENAME(''?'', 1),
COUNT(*) FROM ?'
Be aware that this last method will return multiple result sets (one for each table), so you need to handle it that way in Access or combine them together in a temporary table and then return them as one result set. You can do all of that in a stored procedure.
You cannot use a variable for a table name. The only workaround is dynamic SQL.
However, I suspect that there's a table somewhere in sys which gives the row count for each table, though it may not be kept perfectly up-to-date.
Can't you do this:
SELECT COUNT(t.name)
FROM sys_tables AS t INNER JOIN sys_columns AS c ON t.object_id = c.object_id

update one table using data from another table

I am trying to update my current table by drawing data from another table.
My database (dbo_finance)
column - test
The other database is assestsc and I am going to pull the data from column issuename1,
however I only want to pull the issuename1 when the field [MinSecClass] is = 9.
This is what I wrote
UPDATE dbo_finance
SET [dbo_finance].cusip9 = AssetsC.cusip
FROM dbo_finance INNER JOIN AssetsC ON dbo_finance.test = AssetsC.[IssueName1]
WHERE (AssetsC.MinSecClass = 9)
Thanks, first time really using SQL
Well I would use aliases, it's a good habit to get into:
UPDATE f
SET [dbo_finance].cusip9 = AssetsC.cusip
FROM dbo_finance f
INNER JOIN AssetsC a ON f.test = a.[IssueName1]
WHERE (a.MinSecClass = 9)
Now that will work fine if the assets table will only return one value for cuspid for each record. If this is a one to many relationship you may need to get more complex to truly get the answer you want.
I see several serious design flaws in your table structure. First joins fields that are dependant as something as inherently unstable as issue name are a very poor choice. You want PK and FK field to be unchanging. Use surrogate keys instead and a unique index.
The fact that you have a field called cusp9 indicates to me that you are denormalizing the data. Do you really need to do this? Do you undestand that this update will have to run in a trigger ever time the cuspid assoicated with MinSecClass changes? Whya re you denormalizing? Do you currently have performance problems? A denormalized table like this can be much more difficult to query when you need data from several of these numbered fields. Since you already have the data in the assets table what are you gaining except a maintenance nightmare by duplicating it?
UPDATE dbo_finance
SET cusip9 = (
SELECT A1.cusip
FROM AssetsC AS A1
WHERE dbo_finance.test = A1.IssueName1
AND AssetsC.MinSecClass = 9
)
WHERE EXISTS (
SELECT *
FROM AssetsC AS A1
WHERE dbo_finance.test = A1.IssueName1
AND A1.MinSecClass = 9
);
Because you're using SQL 2008, you can take advantage of the new(ish) MERGE statement.
MERGE INTO dbo_finance
USING (SELECT IssueName1, cusip FROM AssetsC WHERE MinSecClass = 9) AS source
ON dbo_finance.test = source.IssueName1
WHEN MATCHED THEN UPDATE SET dbo_finance.cusip9 = source.cusip;

How can I turn a column name into a result value in SQL Server?

I have a table which has essentially boolean values in a legacy database. The column names are stored as string values in another table so I need to match the column names of one table to a string value in another table. I know there has to be a way to do this directly with SQL in SQL Server but it is beyond me.
My initial thought was to use PIVOT but it is not enabled by default and enabling it would likely be a difficult process with pushing that change to the Production database. I would prefer to use what is enabled by default.
I am considering using COALESCE to translate the boolean value to the string that value that I need. This will be a manual process.
I think I will also use a table variable to insert the results of the first query into that variable and use those results to do the second query. I still have the problem that the columns are on a single row so I wish I could easily pivot the values to put the column names in the result set as strings. But if I could easily do that I could easily write the query with a sub-select.
Any tips are welcome.
Checkout Sysobjects and SysColumns in SQL Server. They are 2 SQL tables that gives you the names of the tables in your DB and the names of the columns that go with that table.
The system view INFORMATION_SCHEMA.COLUMNS will also give you what you want.
You can build a SQL string and then execute that string as a query. Not the prettiest by any means but I think it would work the way you want it to. You would just use a cursor or while loop to build the string.
If you're comfortable with .Net you could just write your own stored proc in your language of choice and manipulate the data in code instead.
Heres a link to get started
CLR Stored Procedures
I'm not quite sure I understand how your design is currently put together (could you post an example?), but the information_schema.columns view will give you a table containing all the column names as string values. If you join your second table against that I think you'll probably get what you need.
For Example, i have a table STATEtbl having 3 columns and i want to get all the column names of this table as ROW values... i use the below query
Select SC.name as Columns from Syscolumns SC
Join Sysobjects SO On SC.id = SO.Id
where Object_name(SO.Id) = 'STATEtbl'
Result of the query:
Columns
--------
State
StateCode
StateFullName