Write INSERT statements with values next to column names? - sql

When writing an INSERT statement with a lot of columns, it would be nice to have the value next to the column name like in an UPDATE statement. Something like:
insert into myTable
set
[col1] = 'xxx',
[col2] = 'yyy',
[col3] = 42
etc...
Are there any tricks to mimic this?
I thought I was onto something with this:
insert into myTable
select
[col1] = 'xxx',
[col2] = 'yyy',
[col3] = 42
etc...
But the aliases aren't actually being associated with the insert table's columns and if someone added a new column to the table it could really screw things up. Anyone have any other ideas of how one could do this?

The closest you'll get would be to specify the columns on the insert (this will protect you from your concern about a new column being added) and alias the values on the select (which gives you some degree of self-documenting code).
insert into myTable
([col1], [col2], [col3])
select 'xxx' as [col1], 'yyy' as [col2], 42 as [col3]

For big inserts, I've done fancy (and perhaps overfly fussy) layouts. Some examples:
INSERT MyTable ( MyTableId, Name, Description, SomeStringData1
,SomeStringData2, SomeStringData3, SomeStringData4, MoreStringData1
,MoreStringData2, MoreStringData3, MoreStringData4, SomeNumericData1
,SomeNumericData2, SomeNumericData3, SomeNumericData4, MoreNumericData1
,MoreNumericData2, MoreNumericData3, MoreNumericData4, BigBlobAA
,BigBlobBB, EnteredAtDate, UpdatedAtDate, RevisedAtDate
,NeedAnotherDate )
values
( #MyTableId, #Name, #Description, #SomeStringData1
,#SomeStringData2, #SomeStringData3, #SomeStringData4, #MoreStringData1
,#MoreStringData2, #MoreStringData3, #MoreStringData4, #SomeNumericData1
,#SomeNumericData2, #SomeNumericData3, #SomeNumericData4, #MoreNumericData1
,#MoreNumericData2, #MoreNumericData3, #MoreNumericData4, #BigBlobAA
,#BigBlobBB, #EnteredAtDate, #UpdatedAtDate, #RevisedAtDate
,#NeedAnotherDate )
This works if you're pretty darn certain that you wont ever be inserting columns or otherwise modifying what is being inserted. It gets gets everything on one screen, and makes it fairly simple to pick out what value goes into which column.
If inserted values are likely to change or are complex (such as case statements), I do the following (outdent all but every fifth item):
INSERT MyTable
(
MyTableId
,Name
,Description
,SomeStringData1
,SomeStringData2
,SomeStringData3
,SomeStringData4
,MoreStringData1
,MoreStringData2
,MoreStringData3
,MoreStringData4
,SomeNumericData1
,SomeNumericData2
,SomeNumericData3
,SomeNumericData4
,MoreNumericData1
,MoreNumericData2
,MoreNumericData3
,MoreNumericData4
,BigBlobAA
,BigBlobBB
,EnteredAtDate
,UpdatedAtDate
,RevisedAtDate
,NeedAnotherDate
)
values
(
MyTableId
,Name
,Description
,SomeStringData1
,SomeStringData2
,SomeStringData3
,SomeStringData4
,MoreStringData1
,MoreStringData2
,MoreStringData3
,MoreStringData4
,case
when something then 'A'
when orOther then 'B'
else 'Z'
end
,SomeNumericData2
,SomeNumericData3
,SomeNumericData4
,MoreNumericData1
,MoreNumericData2
,MoreNumericData3
,MoreNumericData4
,BigBlobAA
,BigBlobBB
,EnteredAtDate
,UpdatedAtDate
,RevisedAtDate
,NeedAnotherDate
)
(After adding that CASE statement, I "counted indents" to make sure I had everything lined up properly.)
It takes a bit of effort to get things layed out properly, but it can make maintenance, support, and subsequent modification simpler.

Basically, no. The syntax for SQL INSERT is to the list all the columns, then all the values. Even if you could find a trick to express the syntax the way you want it would be non-portable and confusing to the next person to have to maintain your code.
If you want a visual mapping of columns to values use something along the lines of Philip's answer, above. But please don't spend any of your valuable time to make your code avoid the standard syntax.

Once you want to insert multiple rows simultaneously, using either 2008 style row constructors, or using the older union all syntax, you tend to be thankful that you don't have to specify the column names for every row.
2008 row constructors:
insert into myTable (col1,col2,col3)
values ('xxx','yyy',42),
('abc','def',19),
('HJK','www',-4)
UNION all:
insert into myTable (col1,col2,col3)
select 'xxx','yyy',42 union all
select 'abc','def',19 union all
select 'HJK','www',-4

The quick solution I came up with was the following:
insert into MyTable
(ColumnName1, ColumnName2, ColumnName3)
values
(/*ColumnName1*/'value1', /*ColumnName2*/'value2', /*ColumnName3*/'value3')

INSERT INTO myTable (col1, col2, col3)
SELECT 'xxx' [col1], 'yyy' [col2], '42' [col3]

Related

SELECT-FROM-IF condition in SQL

I have the following SELECT statement:
SELECT
"project_id" as "ID"
FROM "projects"
but I want this to happen only when I have only distinct values in this column, such as:
COUNT(DISTINCT "project_id") = COUNT("project_id")
else I would like the program to crash with a message "The IDs do not have unique values".
Any ideas how I should tackle this?
I tried different CASE WHEN scenarios but without any luck.
This is more complicated than you think because you are trying to return 2 different datasets from the same query - most RDBMS systems do not like this. The datatype of each column must always be the same, and the number of columns needs to be the same. What you are trying to do is really better suited to the application layer.
That being said, here is a version that is close to what you ask. I have written it using SQL Server but the actual query itself uses standard SQL. First I will make a table variable with some sample data.
DECLARE #Data TABLE (project_id INT);
-- fill the table with all unqiue values
INSERT INTO #Data(project_id) VALUES (1), (2), (3);
SELECT
CASE WHEN COUNT(DISTINCT project_id) = COUNT(project_id) THEN CAST(project_id AS VARCHAR)
ELSE CONCAT(project_id, ' is not unique') END AS [ID]
FROM #Data
GROUP BY project_id;
The output of this looks like:
|ID|
|--|
|1|
|2|
|3|
Now let's look at a version where there are duplicate values:
DECLARE #Data TABLE (project_id INT);
-- fill the table with all unqiue values
INSERT INTO #Data(project_id) VALUES (1), (2), (3);
-- add duplicate values
INSERT INTO #Data(project_id) VALUES (1), (3);
SELECT
CASE WHEN COUNT(DISTINCT project_id) = COUNT(project_id) THEN CAST(project_id AS VARCHAR)
ELSE CONCAT(project_id, ' is not unique') END AS [ID]
FROM #Data
GROUP BY project_id;
And the output looks like:
ID
1 is not unique
2
3 is not unique
Again, this isn't exactly what you asked for, but I am not 100% sure that what you are asking can be accomplished in SQL alone.

How to insert multiple rows into an SQL table when the values for one (or more) of the columns need to be derived from another table?

I cannot for the life of me find an answer to this extremely simple question.
What is the correct way to insert multiple rows into an SQL table when the values for one (or more) of the columns need to be derived from another table?
This is what I'm trying to do, using SQL Anywhere:
INSERT INTO Table (ColName1, ColName2, ColName3FK) VALUES
('foo', 'bar', (SELECT OtherTable.ID FROM OtherTable WHERE SomeCol = 'something')),
('doo', 'dar', (SELECT OtherTable.ID FROM OtherTable WHERE SomeCol = 'something')),
('goo', 'gar', (SELECT OtherTable.ID FROM OtherTable WHERE SomeCol = 'something'));
This throws an error saying 'Invalid value for INSERT' at the second SELECT statement. Sure enough, if I attempt to insert a single line the same way, it works fine:
INSERT INTO Table (ColName1, ColName2, ColName3FK) VALUES
('foo', 'bar', (SELECT OtherTable.ID FROM OtherTable WHERE SomeCol = 'something'));
Also, manually looking up the IDs I need and typing them in works with multiple lines:
INSERT INTO Table (ColName1, ColName2, ColName3FK) VALUES
('foo', 'bar', 42),
('doo', 'dar', 42),
('goo', 'gar', 42);
I have tried assigning the IDs I need to variables and using them in the INSERT statement with no luck (although I haven't spent too much time trying to do that, so I might have used incorrect syntax or sth). I have tried doing a similar thing using the WITH statement as well.
what you can do it's try to get the id outside of the query and after insert the rows like this .
var id = SELECT OtherTable.ID FROM OtherTable WHERE SomeCol = 'something' ;
(look at the id result and get the id )
INSERT INTO Table (ColName1, ColName2, ColName3FK) VALUES
('foo', 'bar', id),
('doo', 'dar',,id ),
('goo', 'gar',id);

Insert data into multiple tables from one select statement in sql

I need to insert data into two different tables via a select statement.This select statement is calling an inline TVF.
What I have so far is :
INSERT INTO #Temp2 (RowNumber, ValFromUser, ColumnName, ValFromFunc, FuncWeight, percentage)
SELECT
RowNumber, #hospitalname, 'hospitalname',
PercentMatch, #constVal, PercentMatch * #constVal
FROM
dbo.Matchhospitalname (#hospitalname)
But there are certain columns that need to be supplied to a permanent table dbo.Cache.
Above mentioned query is called multiple times in the procedure.
Insert into dbo.Cache(StringSearched, ColName, RowId, PercentMatch)
select
ValFromUser, ColumnName, RowNumber, Max(ValFromFunc) as Percentage
from
#Temp2
group by
ValFromUser, ColumnName, RowNumber
Adding data into dbo.Cache separately as above would make all the previously added values to be added as many times as this statement is executed which is of course not desirable.
May be if it is not possible at all to add data to two tables via one select, we can do something like adding only those rows that were added in last insert statement only ?
Can I get some directions on this, please?
Edit : As suggested, I tried using OUTPUT INTO this way but Group by seems to be at the wrong place.The grouped rowsare to be inserted only in dbo.Cache and not in #Temp2
How do I solve this ?
INSERT INTO #Temp2 (RowNumber,ValFromUser,ColumnName,ValFromFunc,FuncWeight,percentage)OUTPUT
INSERTED.ValFromUser,
INSERTED.ColumnName,
INSERTED.RowNumber,
MAX(INSERTED.ValFromFunc)
INTO dbo.CACHE
(StringSearched, ColName, RowId, PercentMatch)
Group By Inserted.ValFromUser, Inserted.ColumnName, Inserted.RowNumber
SELECT RowNumber,#firstname,'firstname',PercentMatch,#constVal,PercentMatch * #constVal FROM dbo.MatchFirstName(#firstname)
You can do it via an output clause or more typically you can put a trigger on a table. In other words you can create an after insert trigger on temp table '#temp2'. I have never seen a trigger on a temp table but its possible. You will have to recreate the trigger every time the temp table is recreated. Remember that #temp2 will only exist (and be visible) in the session that it is created in.

SELECT Query - Can you add data into the second record only?

I have a question for all of the SQL wizards out there and that is can I add data into the second record in a SELECT query only?
I thought I'd ask here first to see if this is even possible? At the moment the first row is filled with mandatory data that needs to stay there (FileVersion and FileName). What I need from the second row is for "2A" to appear in record 2 only, but as you see it's also appearing in 3 & 4.
I've been experimenting with TOP but had little success. I am using T-SQL. If you need any additional info then please just ask :-D.
Many thanks in advance for any help.
There is no such logic as second row. This may change from time to time, so you need some kind of column(s) to determine what defines second row. I used ID in my example.
declare #t table(id int, col1 varchar(20), col2 varchar(20))
insert #t values(1, 'a', 'a')
insert #t values(2, 'b', 'b')
insert #t values(3, 'c', 'c')
SELECT id,
case when row_number() over (order by id)= 2 then '2A' else col1 end col1, col2
FROM #t

Building a temp table /map

I'm working on a stored procedure in SQL Server 2000 with a temp table defined like this:
CREATE TABLE #MapTable (Category varchar(40), Code char(5))
After creating the table I want to insert some standard records (which will then be supplemented dynamically in the procedure). Each category (about 10) will have several codes (typically 3-5), and I'd like to express the insert operation for each category in one statement.
Any idea how to do that?
The best idea I've had so far is to keep a real table in the db as a template, but I'd really like to avoid that if possible. The database where this will live is a snapshot of a mainframe system, such that the entire database is blown away every night and re-created in a batch process- stored procedures are re-loaded from source control at the end of the process.
The issue I'm trying to solve isn't so much keeping it to one statement as it is trying to avoid re-typing the category name over and over.
DJ's is a fine solution but could be simplified (see below).
Why does it need to be a single statement?
What's wrong with:
insert into #MapTable (category,code) values ('Foo','AAAAA')
insert into #MapTable (category,code) values ('Foo','BBBBB')
insert into #MapTable (category,code) values ('Foo','CCCCC')
insert into #MapTable (category,code) values ('Bar','AAAAA')
For me this is much easier to read and maintain.
Simplified DJ solution:
CREATE TABLE #MapTable (Category varchar(40), Code char(5))
INSERT INTO #MapTable (Category, Code)
SELECT 'Foo', 'AAAAA'
UNION
SELECT 'Foo', 'BBBBB'
UNION
SELECT 'Foo', 'CCCCC'
SELECT * FROM #MapTable
There's nothing really wrong with DJ's, it just felt overly complex to me.
From the OP:
The issue I'm trying to solve isn't so much keeping it to one statement as it
is trying to avoid re-typing the category name over and over.
I feel your pain -- I try to find shortcuts like this too and realize that by the time I solve the problem, I could have typed it long hand.
If I have a lot of repetitive data to input, I'll sometimes use Excel to generate the insert codes for me. Put the Category in one column and the Code in another; use all of the helpful copying techniques to do the hard work
then
="insert into #MapTable (category,code) values ('"&A1&"','"&B1&"')"
in a third row and I've generated my inserts
Of course, all of this is assuming that the Categories and Codes can't be pulled from a system table.
insert into #maptable (category, code)
select 'foo1', b.bar
from
( select 'bar11' as bar
union select 'bar12'
union select 'bar13'
) b
union
select 'foo2', b.bar
from
( select 'bar21' as bar
union select 'bar22'
union select 'bar23'
) b
This might work for you:
CREATE TABLE #MapTable (Category varchar(40), Code char(5))
INSERT INTO #MapTable
SELECT X.Category, X.Code FROM
(SELECT 'Foo' as Category, 'AAAAA' as Code
UNION
SELECT 'Foo' as Category, 'BBBBB' as Code
UNION
SELECT 'Foo' as Category, 'CCCCC' as Code) AS X
SELECT * FROM #MapTable
Here's the notation I ended up using. It's based on Arvo's answer, but a little shorter and uses cAse to help make things clearer:
SELECT 'foo1', b.code
FROM ( select 'bar11' as code
union select 'bar12'
union select 'bar13' ) b
UNION SELECT 'foo2', b.code
FROM ( select 'bar21' as code
union select 'bar22'
union select 'bar32' ) b
This way highlights the category names a little, lines up codes vertically, and uses less vertical space.