Simple scenario table with single column:
CREATE OR REPLACE TABLE t1(c VARCHAR(200));
INSERT INTO t1(c) SELECT repeat('a',100); -- works
INSERT INTO t1(c) VALUES (repeat('a',100)); -- error
INSERT INTO t1(c) SELECT space(100); -- works
INSERT INTO t1(c) VALUES (space(100)); -- error
SELECT c, LENGTH(c) FROM t1;
SQL compilation error: Invalid expression [LPAD('', CAST(100 * (CAST(LENGTH('a') AS NUMBER(18,0))) AS NUMBER(21,0)), 'a')] in VALUES clause
Is there a limitation which expressions/function calls could be used with INSERT ... VALUES or is it simply a bug?
It would appear that those expressions would be simple string functions, but looking at the way the compiler translated the function, it no longer appears to be a simple function.
Most simple arithmetic expressions and string functions can be evaluated at compile time, but most other expressions cannot.
Why don't you use variables?
set my_spaces=(SELECT space(100));
set my_aaaa= (SELECT repeat('a',100));
CREATE OR REPLACE TABLE t1(c VARCHAR(200));
INSERT INTO t1(c) SELECT repeat('a',100); -- works
INSERT INTO t1(c) VALUES ($my_aaaa); -- works
INSERT INTO t1(c) SELECT space(100); -- works
INSERT INTO t1(c) VALUES ( $my_spaces ); -- works
SELECT c, LENGTH(c) FROM t1;
Related
insert into ASSET_MAIN_CATEGORIES values(select max(sno) from ASSET_MAIN_CATEGORIES,
'PROD','AC HMU','AC_HMU','PRODUCT','99CS002','','NR','LKO',1);
I wanted to insert max of SNO as column value. But It is showing "Missing Expression Error"
How can I achieve this.
Any Help would be appreciated.
There are at least ways of doing it:
INSERT INTO ... VALUES with embedded select in parentheses so that database will evaluate it:
insert into ASSET_MAIN_CATEGORIES values(
(select max(sno) from ASSET_MAIN_CATEGORIES),
'PROD','AC HMU','AC_HMU','PRODUCT','99CS002','','NR','LKO',1
);
INSERT INTO ... SELECT since all other data is static:
insert into ASSET_MAIN_CATEGORIES
select
max(sno),
'PROD','AC HMU','AC_HMU','PRODUCT','99CS002','','NR','LKO',1
from ASSET_MAIN_CATEGORIES
;
Note that if you do not specify which columns you are populating in ASSET_MAIN_CATEGORIES database assumes that you're feeding values for all of them in the order that they were created in this table.
You need to surround select max with brackets, because it's a SQL statement:
insert into ASSET_MAIN_CATEGORIES values((select max(sno) from ASSET_MAIN_CATEGORIES), 'PROD','AC HMU','AC_HMU','PRODUCT','99CS002','','NR','LKO',1);
If you have an error, prefer to add column names in insert statement
this will work :-
i have checked with my tables its working ,sample code to understand:
insert into b(col1,col2) select 7,(select max(col2) from b)
from dual;
what code you require:-
insert into ASSET_MAIN_CATEGORIES(col1,col2,....,col10)
select (select max(col2) from ASSET_MAIN_CATEGORIES), 'PROD','AC
HMU','AC_HMU','PRODUCT','99CS002','','NR','LKO',1)
from dual;
There are a multitude of questions relating to the "Each GROUP BY expression must contain at least one column that is not an outer reference." error, and the inclusion of a constant value in the GROUP BY clause is often the cause of the error.
I'm actually converting some SQL from a database that allows constants in the GROUP BY clause, so I'm wondering two things:
Why does MS SQL Server not allow constants in the GROUP BY clause?
Why does putting a constant in the GROUP BY clause produce this error and not a "Don't put constants in your GROUP BY clause, idiot." error?
Here's a quick example of the error occurring. The following code bombs out on the third SELECT statement, with the incongruous error message.
DECLARE #demo TABLE
( groupCol1 int
, groupCol2 int);
INSERT INTO #demo VALUES(1,1);
INSERT INTO #demo VALUES(1,1);
INSERT INTO #demo VALUES(2,1);
INSERT INTO #demo VALUES(1,2);
INSERT INTO #demo VALUES(1,2);
INSERT INTO #demo VALUES(2,2);
INSERT INTO #demo VALUES(3,1);
INSERT INTO #demo VALUES(3,1);
INSERT INTO #demo VALUES(1,3);
INSERT INTO #demo VALUES(1,3);
SELECT * FROM #demo;
SELECT * FROM #demo GROUP BY groupCol1, groupCol2;
SELECT *, 'x' FROM #demo GROUP BY groupCol1, groupCol2, 'x';
The error is produced because the query contains a logical error. You have a GROUP BY expression that is equal for all rows. It therefore doesn't meaningfully divide the result set into groups. Some database systems are more tolerant of logical errors and will try to produce a result set. SQL Server isn't very tolerant of such errors. It wants you to really think hard and actually tell it what you want it to do.
You can include values that are constant in many places in T-SQL - but not in places where they do not make a logical difference.
The problem im trying to solve is about avoiding duplicate data getting into my table. I'm using xml to send bulk data to a stored procedure. The procedure I wrote works with 100, 200 records. But when it comes to 20000 of them there is a time out exception.
This is the stored procedure:
DECLARE #TEMP TABLE (Page_No varchar(MAX))
DECLARE #TEMP2 TABLE (Page_No varchar(MAX))
INSERT INTO #TEMP(Page_No)
SELECT
CAST(CC.query('data(PageId)') AS NVARCHAR(MAX)) AS Page_No
FROM
#XML.nodes('DocumentElement/CusipsFile') AS tt(CC)
INSERT INTO #TEMP2(Page_No)
SELECT Page_No
FROM tbl_Cusips_Pages
INSERT INTO tbl_Cusips_Pages(Page_No, Download_Status)
SELECT Page_No, 'False'
FROM #TEMP
WHERE Page_No NOT IN (SELECT Page_No FROM #TEMP
INTERSECT
SELECT Page_No FROM #TEMP2)
How can I solve this? Is there a better way to write this procedure?
As was already suggested, NVARCHAR(MAX) column/variable is very slow and has limited options. If you can change it, it would help a lot.
MERGE tbl_Cusips_Pages
USING (
SELECT
CAST(CC.query('data(PageId)') AS NVARCHAR(4000))
FROM
#XML.nodes('DocumentElement/CusipsFile') AS tt(CC)
) AS source (Page_No)
ON tbl_Cusips_Pages.Page_No = source.Page_No
WHEN NOT MATCHED BY TARGET
THEN INSERT (Page_No, Download_Status)
VALUES (source.Page_No, 'false')
Anyway, your query is not that bad either, just put the queries directly into the third one (TEMP2 one for sure) instead of inserting the data into the table variables. Table variables are quite slow in comparison.
Replace last INSERT Statement with following Script, I have replace IN Clause With NOT EXISTS that may help you for better performance.
DECLARE #CommanPageNo TABLE (Page_No varchar(MAX))
INSERT INTO #CommanPageNo SELECT Page_No FROM #TEMP
INTERSECT
SELECT Page_No FROM #TEMP2
INSERT INTO tbl_Cusips_Pages(Page_No, Download_Status)
SELECT Page_No, 'False'
FROM #TEMP
WHERE NOT EXISTS (SELECT 1 FROM #CommanPageNo WHERE Page_No=#CommanPageNo.Page_No)
Let's say I have a table
my_table(id int identity(1,1) not null primary key, data varchar(100))
I want to write a procedure that inserts a new row into that table and returns id.
I tried
DECLARE #new_id INT;
SELECT #new_id = id FROM
(
INSERT INTO my_table(data) OUTPUT inserted.id VALUES ('test')
) as NewVal(id)
That code doesn't work (I got "A nested INSERT, UPDATE, DELETE, or MERGE statement is not allowed in a SELECT statement that is not the immediate source of rows for an INSERT statement."). However, if I use a table variable, I can do
DECLARE #new_id INT;
DECLARE #tmp_table TABLE(int id);
INSERT INTO #tmp_table
SELECT id FROM
(
INSERT INTO my_table(data) OUTPUT inserted.id VALUES ('test')
) as NewVal(id);
// OR
INSERT INTO my_table(data) OUTPUT inserted.id INTO #tmp_table VALUES ('test') ;
SELECT #new_id = id FROM #tmp_table;
Is it possible to achieve the same functionality without using table variable ?
UPDATE
Thanks for quick responses, +1 to everyone for solution with SCOPE_IDENTITY.
That's probably my fault, I should have asked the question clearly - I do use MERGE (an example would be much longer, so I posted INSERT instead) , not INSERT so SCOPE_IDENTITY doesn't really work for me.
A bit shorter version than nesting in a insert statement is using output...into.
declare #tmp_table table(actiontaken nvarchar(10), id int);
merge my_table
using (values ('test')) as S(data)
on 0=1
when not matched then
insert (data) values (S.data)
output $action, inserted.id into #tmp_table;
I do believe that you should use a table variable from the merge. The output may contain more than one row.
Yes, you can just return SCOPE_IDENTITY after the insert (this is safer than ##IDENTITY due to the scoping differences).
i.e.
INSERT my_table (data) VALUES ('test')
SELECT SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY() will get the last inserted identity value in the scope.
If you use
select ##identity
you will get the last value entered into the identity column from anywhere.
If you want the value entered into the column from the script that just ran, you should use
select SCOPE_IDENTITY()
If you just want the last value inserted into the identity column for a given table from any statement in any session, you should use
select IDENT_CURRENT('tablename')
select ##identity or select SCOPE_IDENTITY() will return the value you're looking for
Let's assume there is such table in SqlServer 2008 database:
CREATE TABLE [dbo].[Test] (
[TableId] [int] IDENTITY(1,1) NOT> NULL,
[Data] [xml] NOT NULL
)
and also I have such table-valued function to parse column Data in my table:
ALTER FUNCTION [dbo].[fnParseTable] (#header XML)
RETURNS #parsedTable TABLE (
[Type] NVARCHAR(50),
[Value] NVARCHAR(50)
)
AS BEGIN
--parse xml here
RETURN
END
Can I concatenate all results of this function for each column of the table?
I need something like this:
SELECT UNION fnParseTable(Data) FROM dbo.Test
PS. I know I can do it using cursor, but I want to make sure there are no any easier solutions
You don't need a table valued function, use XPath to extract these values directly in a SELECT statement
SELECT
Data.query('data(/xpath/to[#your="type"])') AS type,
Data.query('data(/xpath/to[#your="value"])') AS value
FROM Test
/* JOINs, WHERE HAVING, GROUP BY and/or ORDER BY clauses */
query() executes an XPath expression, while data() extracts a value from the resulting XML node.
Update
MSDN Link
DECLARE #testTable TABLE(
XmlData XML
)
INSERT INTO #testTable (XmlData)
VALUES ('<row><node><key>key11</key><value>value11</value></node><node><key>key12</key><value>value12</value></node></row>')
INSERT INTO #testTable (XmlData)
VALUES ('<row><node><key>key21</key><value>value21</value></node><node><key>key22</key><value>value22</value></node></row>')
INSERT INTO #testTable (XmlData)
VALUES ('<row><node><key>key31</key><value>value31</value></node><node><key>key32</key><value>value32</value></node></row>')
SELECT
nref.value('key[1]', 'nvarchar(50)') AS [key],
nref.value('value[1]', 'nvarchar(50)') AS value
FROM #testTable CROSS APPLY XmlData.nodes('//node') AS R(nref)
Result
key11 value11
key12 value12
key21 value21
key22 value22
key31 value31
key32 value32
Orangepips answer seems the adequate solution for your problem.
But taking the question verbatim: YES there is a way to create a suitable aggregate function using CLR in SQL-Server 2005+.
But it is admittedly a bit complicated.
You have to compile the c# code, you find at Invoking CLR User-Defined Aggregate Functions.
I succeeded in running that example, but I prefer to use orangepips xpath solutions when suitable, because it is just T-SQL und requires no
sp_configure 'clr enabled',1
reconfigure