It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a problem with create query
=========== ============================ =================
ValueTable1 ValueTable2 FilterTable1
=========== ============================ =================
Id |Name Id | RefNo || Property| Value Id|Property|Operator|Value
1 |X 1 1 P1 500 1 P1 > 100
2 1 P2 300 1 P2 = 300
3 1 P3 200 1 P3 <> 100
2 |Y 4 2 P1 250
5 2 P2 100
6 2 P3 200
I have 2 table ValueTable and 1 filter table in my database
I wanna create a query that can filter Valuetabl2 from properties and operators in FilterTable1
I couldnt create it.Is there any way?
Maybe I did misunderstood your question...
You want do apply those Filters on FilterTable1 on the values of table ValueTable2, right?
doing AND condition... with the filters..
to identify what RefNo fill all conditions.. I am right? or did misunderstood at this point?
and this RefNo references the table ValueTable1, right? or I did misunderstood??
if I did undestood right
can try something like this...
create table ##ValueTable1 (id int , name varchar)
insert into ##ValueTable1
values (1,'X'),(2,'Y')
create table ##ValueTable2 (id int , RefNo int, Property nvarchar(max),value int)
insert into ##ValueTable2
values
( 1 , 1 ,'P1', 500),
( 2 , 1 ,'P2', 300),
( 3 , 1 ,'P3', 200),
( 4 , 2 ,'P1', 250),
( 5 , 2 ,'P2', 100),
( 6 , 2 ,'P3', 200)
create table ##FilterTable1 (id int, Property nvarchar(max),Operator nvarchar(max) , value int)
insert into ##FilterTable1
values
(1,'P1','>', 100),
(1,'P2','=', 300),
(1,'P3','<>',100)
declare #sql nvarchar(max)
set #sql = ''
select #sql = #sql + ' intersect select RefNo from ##ValueTable2 where Property = ''' +Property+''' and value '+Operator+CAST(value as varchar(max))
from ##FilterTable1
where id = 1
set #sql = 'select * from ##ValueTable1 T inner join ('+STUFF(#sql,1,10,'') +')V on V.RefNo = T.id'
--just stuff #sql if ##ValueTable1 doesnt matter
--set #sql = STUFF(#sql,1,10,'')
exec(#sql)
drop table ##ValueTable2
drop table ##FilterTable1
drop table ##ValueTable1
my resultset achieved is...
id name RefNo
1 X 1
You have to create a string for your sql statement in a procedure loop for example and then execute it.
See here how to execute it: http://msdn.microsoft.com/de-de/library/ms188332.aspx.
You can not create dynamic sql queries directly.
So create a procedure, create a local cursor to loop to your ValueTable2 and for each property check the operator in FilterTable1, then create a new sql string which you execute then.
EDIT
I don't fully understand your requirement, but this example should give you an approach:
DECLARE #operator varchar(2)
DECLARE #value varchar(255)
DECLARE #sql varchar(max)
DECLARE cur1 CURSOR FOR
SELECT [operator], CAST([value] AS varchar(255))
FROM ValueTable2 v
JOIN FilterTable1 f ON v.Property = f.Property
OPEN cur1
FETCH NEXT FROM cur1
INTO #operator, #value
WHILE ##FETCH_STATUS = 0
BEGIN
#sql = 'SELECT * FROM ValueTable2 WHERE [value] ' + #operator + ' ' + #value
PRINT #sql
-- EXEC sp_executesql #sql
FETCH NEXT FROM cur1 INTO #operator, #value
END
DEALLOCATE cur1
Well, i would try NOT to use CURSORS and/or Dynamic SQL. Cursors are possible of creating serious locking issues, and dynamic SQL is always being (re)compiled, so doing that together is a no go in my point of view.
However, using a temp table with a while loop might be better in stead of cursors. For logic i would say it must be possible to use some if statements..
I made this SQLFiddle (http://sqlfiddle.com/#!3/8a4e3/) to show you what i mean
Related
I need to document metadata for the fields of multiple tables. I intend to do this by appending the tables with extended properties. The only way I've done this in the past is using the built-in stored procedure which looks like this:
ALTER procedure [sys].[sp_addextendedproperty]
#name sysname,
#value sql_variant = NULL,
#level0type varchar(128) = NULL,
#level0name sysname = NULL,
#level1type varchar(128) = NULL,
#level1name sysname = NULL,
#level2type varchar(128) = NULL,
#level2name sysname = NULL
as
declare #ret int
if datalength(#value) > 7500
begin
raiserror(15097,-1,-1)
return 1
end
if #name is null
begin
raiserror(15600,-1,-1,'sp_addextendedproperty')
return (1)
end
execute #ret = sys.sp_validname #name
if (#ret <> 0)
begin
raiserror(15600,-1,-1,'sp_addextendedproperty')
return (1)
end
BEGIN TRANSACTION
begin
EXEC %%ExtendedPropertySet().AddValue(Name = #name, Value =
#value, Level0type = #level0type, Level0name = #level0name, Level1type =
#level1type, Level1name = #level1name, Level2type = #level2type,
Level2name = #level2name)
IF ##error <> 0
begin
COMMIT TRANSACTION
return (1)
end
end
COMMIT TRANSACTION
return (0)
The way I've done this in the past is by just passing strings to the variables:
EXEC sys.sp_addextendedproperty
#name=N'desc_en',
#value=N'Sex. 1 denotes male, 2 denotes female.' ,
#level0type=N'SCHEMA',
#level0name=N'dbo',
#level1type=N'TABLE',
#level1name=N'MyTable',
#level2type=N'COLUMN',
#level2name=N'sex'
GO
But now that I have to do this for multiple tables (and ideally write some re-usable procedure), I'm attempting to write a function that takes dynamic (i.e. SELECT) arguments.
For instance, take the following tables. The first has a couple of fields that need extended properties, whereas the other table has two fields containing that information:
dbo.users:
id | name | sex | year
-------------------------
1 | 'john' | 1 | 2019
2 | 'jane' | 2 | 2019
dbo.metadata:
id | property_en | desc_en
-------------------------
1 | sex | Sex. 1 denotes male, 2 denotes female
2 | year | The year of the event record
desired ep:
EXEC sys.sp_addextendedproperty
#name=N'desc_en',
#value=N'Sex. 1 denotes male, 2 denotes female' ,
#level0type=N'SCHEMA',
#level0name=N'dbo',
#level1type=N'TABLE',
#level1name=N'users',
#level2type=N'COLUMN',
#level2name=N'sex'
Any suggestions to derive EPs directly from a source and metadata table like that as an iteration? The first set of arguments have to come from the source table itself (i.e., information about the schema, table name and column name), whereas the second from the metadata table itself (i.e. property_en and desc_en).
Essentially, the first table (t1) provides:
#level0name
#level1name
And t2:
#name
#value
And the join is on #level2name, in other words where t1.clmns.name = t2.property_en
Here we go. Dynamic sql like this is a bit tricky if you haven't done it very much. In essence I am just using the system tables to get the information needed based on the data in your metadata table. Then we just build up a big sql string with a bunch of stored proc calls in it. Finally we just fire it off to run.
Please note, this will find any column that matches in any table based on the name in metadata. If you need to refine that for something like only tables with both columns or something you will need to adjust this a little bit.
create table metadata
(
id int
, property_en sysname
, desc_en varchar(500)
)
insert metadata values
(1, 'sex', 'Sex. 1 denotes male, 2 denotes female')
, (2, 'year', 'The year of the event record')
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'EXEC sys.sp_addextendedproperty #name=N''' + d.property_en + ''', #value=N''' + d.desc_en + ''', #level0type=N''SCHEMA'', #level0name=N''' + s.name + ''', #level1type=N''TABLE'', #level1name=N''' + t.name + ''', #level2type=N''COLUMN'', #level2name=N''' + d.property_en + ''';'
from metadata d
join sys.columns c on c.name = d.property_en
join sys.tables t on t.object_id = c.object_id
join sys.schemas s on s.schema_id = t.schema_id
select #SQL
--once you are comfortable that the dynamic sql works uncomment the line below.
--exec sp_executesql #SQL
Here is an 'instead of' trigger based mechanism based on prior work by Cade Roux and "Phil Factor" (credits in the comments)
https://github.com/phrrngtn/rule4/blob/main/sql/extended_properties.sql
This creates some views RULE4.extended_property and RULE4.extended_property_aux that you can treat as normal table and insert/update/delete extended properties. The instead of trigger proxies this onto calls to the corresponding extended properties sproc.
I have this table:
-----------------------------------------------
code intvalue checkrole result
-----------------------------------------------
A01 14 A02-A03 true
A02 24 A04 false
A03 10 A04 false
A04 12 A02/2 true
I would like to fill the column result with a query or sp, based on the role described into the column checkrole, something like Excel, any ideas?
I apologize to everyone, I explained myself wrongly.
For example:
A01 14 A02-A03 true
in this case, I would like to interpret the role and get 24-10 = 14 ie true
UPDATE 2:
Hello everyone and thanks for your interest. HABO, I'm working in this direction "substitute the values into the expression (" 24-10 ") ....":
TBL
code intValue checkRule result
A01 14 select A02-A03 from Table_1 NULL
A02 24 select A04 from Table_1 NULL
A03 10 select A04 from Table_1 NULL
A04 12 select A02 / 2 from Table_1 NULL
SP
CREATE PROCEDURE [dbo]. [Test]
AS
BEGIN
DECLARE #code VARCHAR (50)
DECLARE #intvalue INT
DECLARE #checkrule VARCHAR (50)
DECLARE #cTbl AS CURSOR
SET #cTbl = CURSOR FOR SELECT code
, intValue
, checkRule
FROM [dbo]. [Table_1]
OPEN #cTbl FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
WHILE ## FETCH_STATUS = 0
BEGIN
declare #statement nvarchar (4000), #Result int, #Parm nvarchar (20)
SET #statement = 'select #Result = 11 + 7'
SET #Parm = '#Result int output'
EXEC sp_executesql #statement, #Parm, # Result OUT
print #Result
FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
END
CLOSE #cTbl
DEALLOCATE #cTbl
END
UPDATE 3:
It was what I was looking for. I'm not an expert, but I learn from mistakes and I thank those who teach me something new, thanks to all those who participated, and above all thanks to HOBO, good evening
I would probably write a function that would create a list of values from your checkRole (eg it would give me list 'A02', 'A03', 'A04' from A02-A04 and then I would write an update statement with 'contains' statement in it
using the table that you show you can update the values of result column with the next query
UPDATE TableName
SET result = CASE
WHEN checkrole = 'A02-A03' THEN "true"
WHEN checkrole = 'A04' THEN "false"
WHEN checkrole = 'A02/2' THEN "true"
ELSE "setDefaultValue"
END
WHERE code in (A01,A02,A03,A04);
at the last line you specify the code value of row to update. If you execute this query with the column result empty, it will complete the table and result into the same data as the one you post above.
(The query work good on Mysql)
Executive Summary: This is a terrible idea. Back away slowly and no one gets hurt.
Disclaimer: I'm not responsible for the costs of any therapy required after reading beyond this point.
From your Update 2 it appears that you have control over the format of CheckRule. It is much easier to require that the user delimit the Codes, e.g. using '«A04»' rather than 'A04', instead of trying to find them using TSQL. You don't need to worry about problems like substituting 'A02' with 66 when the CheckRule is 'A026 - BA02' and getting '666 - B66'.
What follows is simply dreadful. It demonstrates a brute force approach to taking expressions with delimited "variables", e.g. '«A04»', replacing all occurrences of each "variable" with its integer value, evaluating the resulting expression and retrieving the result.
Instead of using something as distasteful as a cursor, it uses two nested cursors to waddle through the CheckRules and Codes. It would be more efficient to parse the "variables" out of the expression and substitute values as needed rather than using a try-everything approach, but TSQL doesn't excel at implementing parsers.
For extra credit it also brings along the blessing of embracing SQL Injection. Try a CheckRule like 'A01; select 1 / 0;'. Don't try one like '-1; drop database LuckyGuess;'.
-- Sample data.
declare #Samples as Table
( SampleId Int Identity, Code NVarChar(8), IntValue Int, CheckRule NVarChar(64), Result Bit );
insert into #Samples ( Code, IntValue, CheckRule ) values
( N'A01', 14, N'«A02» - «A03»' ),
( N'A02', 24, N'«A04»' ),
( N'A03', 10, N'«A04»' ),
( N'A04', 12, N'«A02» / 2' );
select * from #Samples;
-- Process the data.
declare Oy cursor forward_only fast_forward read_only
for select SampleId, Code, IntValue, CheckRule from #Samples;
declare #SampleId as Int, #Code as NVarChar(8), #IntValue as Int, #CheckRule as NVarChar(64);
declare Vey cursor forward_only fast_forward read_only
for select Code, IntValue from #Samples;
declare #VariableCode as NVarChar(8), #VariableIntValue as Int
-- For each row's CheckRule ...
open Oy;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
while ##Fetch_Status = 0
begin
-- Copy the CheckRule so that we can build the #Expression to evaluate.
declare #Expression as NVarChar(64) = #CheckRule;
-- For each Code that could appear in an #Expression ...
open Vey;
fetch next from Vey into #VariableCode, #VariableIntValue;
while ##Fetch_Status = 0
begin
-- Replace any occurrences of the Code in the #Expression with the corresponding integer value.
set #Expression = Replace( #Expression, N'«' + #VariableCode + N'»',
Cast( #VariableIntValue as NVarChar(10) ) );
fetch next from Vey into #VariableCode, #VariableIntValue;
end;
close Vey;
-- Make the #Expression an executable statement.
set #Expression = N'set #IntResult = ' + #Expression;
declare #Result as Int;
-- Evaluate the #Expression and get the #Result .
execute sp_executesql #Expression, N'#IntResult Int output', #IntResult = #Result output;
select #SampleId as SampleId, #Code as Code, #IntValue as IntValue, #CheckRule as CheckRule,
#Expression as Expression, #Result as Result;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
end;
close Oy;
deallocate Oy;
In the event it isn't clear, don't. Just don't.
I want to know, if exist a way to create a CURSOR with a variable column names
Example:
I have this table:
Translation image for text
ID | TEST1 | TEST2 | TEST3
7 1 3 1
8 2 3 4
9 3 4 5
10 3 3 1
11 2 3 4
12 3 4 5
13 1 3 1
14 2 3 4
15 3 4 5
SQL Code:
DECLARE
#count int,
#columnX varchar(5),
#aux2previous int,
#aux2 int,
#aux1 int,
#columnXResult int,
#id int;
SET #aux2previous = 0;
SET #count = 1;
SET #columnX = 'test' + count;
DECLARE cursor1 CURSOR FOR
SELECT ID, #columnX FROM table
OPEN cursor1
FETCH NEXT FROM cursor1 INTO #id,#columnXResult
...
SET #aux1 = #columnXResult+ #aux2previous
SET #aux2 = #aux2previous + 1
SET #string = 'SXW_'+#columnX+'_'+#aux1+'<>'+#aux2
INSERT INTO tblAuxiliary VALUES(#aux1,#aux2,#string)
SET #count = #count + 1;
SET #aux2previous = #aux2
...
Translation image for text
Foreach row in my first table i have a new row here:
for column Test1
AUX1 | AUX2 | STRING
1 1 SXW_Test1_1<>1
3 2 SXW_Test1_3<>2
for column Test2
AUX1 | AUX2 | STRING
3 1 SXW_Test2_3<>1
6 4 SXW_Test2_6<>4
for column Test3
AUX1 | AUX2 | STRING
1 1 SXW_Test3_1<>1
5 2 SXW_Test3_5<>2
when i do with #columnX
SELECT ID, #columnX FROM table
i got this error:
Conversion failed when converting the varchar value 'test1' to data type int
I see something like #sql = 'select '+#columnX ..., but i need to exec it and i can't do this with cursor.
It is example how use dynamic sql and cursor:
create table test (
t1 int not null,
t2 varchar(10) not null
)
declare #query varchar(1000)
select #query = '
declare #column int
declare query cursor for
select t'+cast(1 as varchar(1))+' from test
open query
fetch next from query into #column
while(##FETCH_STATUS=0)
begin
select #column
fetch next from query into #column
end
close query
deallocate query
'
exec (#query)
There's no way to create a cursor with variable column names unless you use dynamic SQL: you build up a string of SQL code and use EXEC sp_executesql to run it. It's messy and difficult to get right, but you can do it with some trial and error (preferably on a development system, not in production).
FWIW, I think some of the comments above a being a little harsh because they've forgotten what it's like to start out in their field: you're doing fine. Cursors are often misused by web developers who think procedurally instead of "set based", but I still use them occasionally where they make sense (or are just easier). If you want, you can post a new question with more details about what you're trying to accomplish and ask "how can I get rid of this cursor". But if the performance with the cursor is okay, don't worry about it.
HTH!
Check this out.
DECLARE #table TABLE(ID INT,test1 INT,test2 INT,test3 INT)
INSERT INTO #TABLE VALUES
(7,1,3,1),(8,2,3,4),(9,3,4,5),(10,3,3,1),
(11,2,3,4),(12,3,4,5),(13,1,3,1),(14,2,3,4),
(15,3,4,5)
SELECT top 2
t.test1 AUX1,(t.test1+T.prev_test1) AUX2,concat('SXW_Test1_',t.test1,'<>',(t.test1+T.prev_test1)) as string FROM
(
SELECT test1,
LAG(test1,1,0) OVER (ORDER BY ID) prev_test1
FROM #table
) t
union all
SELECT top 2
t.test2 AUX1,(t.test2+T.prev_test2) AUX2,concat('SXW_Test2_',t.test2,'<>',(t.test2+T.prev_test2)) as string FROM
(
SELECT test2,
LAG(test2,1,0) OVER (ORDER BY ID) prev_test2
FROM #table
) t
union all
SELECT top 2
t.test3 AUX1,(t.test3+T.prev_test3) AUX2,concat('SXW_test3_',t.test3,'<>',(t.test3+T.prev_test3)) as string FROM
(
SELECT test3,
LAG(test3,1,0) OVER (ORDER BY ID) prev_test3
FROM #table
) t
I have a known target set of records in a large Oracle DB table that I want to pull. Rather than querying them by ID though, I want to find a a column within the table that holds a value(s) that is only assigned to the known set and not assigned to any records that are not in the set, thereby giving me a value to key off of.
Example:
Target IDs: 1, 2, 3
ID Color Dir Size
1 red up S
2 red up M
3 red down L
-----------------------
4 red left S
5 blue left S
6 red left M
7 red right M
8 blue right M
In this scenario, the solution I'm looking for is the "Dir" column, as values up and down are exclusive to records in the desirable set and cover the entire set.
The table I'm working with has 80,000+ records and 100+ columns, so I'm looking for a way to perform this investigation in an automated manner, whether it be by SQL script or with tools like SSIS/SSAS 2008, Excel, PowerShell, etc. What SQL functions and/or utilities can help in this process?
I know this is a long shot, but I have a solution for you that should work in SQL Server. Of course, the caveat being that you would need to get your data from Oracle into SQL Server first.
There are a couple methods listed here:
https://dba.stackexchange.com/questions/23782/what-is-the-easiest-way-to-move-data-from-oracle-to-sql-server
If you manage to do that, you can give this a try:
IF OBJECT_ID('TestTable1', 'U') IS NOT NULL DROP TABLE TestTable1;
CREATE TABLE TestTable1 (
ID INT IDENTITY(1, 1) PRIMARY KEY,
Color VARCHAR(10),
Dir VARCHAR(10),
Size VARCHAR(10)
);
INSERT INTO TestTable1 (Color, Dir, Size)
VALUES ('red', 'up', 'S'),
('red', 'up', 'M'),
('red', 'down', 'L'),
('red', 'left', 'S'),
('blue', 'left', 'S'),
('red', 'left', 'M'),
('red', 'right', 'M'),
('blue', 'right', 'M');
DECLARE #tableName sysname = 'TestTable1';
DECLARE #targetIDs VARCHAR(MAX) = '1, 2';
-- Get all of the columns associated with the specified table:
DECLARE #targetObjectID INT = OBJECT_ID(#tableName, 'U');
DECLARE #ColumnNames TABLE (
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
ColumnName sysname NOT NULL,
HasOverlap BIT NULL
);
INSERT INTO #ColumnNames (ColumnName)
SELECT c.name
FROM sys.columns c
WHERE c.object_id = #targetObjectID
AND c.name <> 'ID';
-- Define a template to use for searching column values for overlap:
DECLARE #columnTestSQL NVARCHAR(MAX) = '
WITH TargetValues AS (
-- This produces a list of values for the target IDs:
SELECT DISTINCT <ColumnName> [Val]
FROM <TableName>
WHERE ID IN (<TargetIDList>)
)
SELECT #hasOverlap_OUT = 1
FROM <TableName> t
WHERE
-- Here we check for overlap with other IDs:
t.ID NOT IN (<TargetIDList>)
AND EXISTS (
SELECT 1
FROM TargetValues tv
WHERE tv.Val = t.<ColumnName>
);
';
SET #columnTestSQL = REPLACE(#columnTestSQL, '<TableName>', #tableName);
SET #columnTestSQL = REPLACE(#columnTestSQL, '<TargetIDList>', #targetIDs);
-- Set up for loop:
DECLARE #curID INT = 1;
DECLARE #maxID INT = (SELECT MAX(ID) FROM #ColumnNames);
DECLARE #curColumnName sysname;
DECLARE #curHasOverlap BIT;
DECLARE #curSQL NVARCHAR(MAX);
-- Go through each column:
WHILE #curID <= #maxID BEGIN
-- Initialize this iteration:
SELECT
#curColumnName = cn.ColumnName,
#curHasOverlap = 0
FROM #ColumnNames cn
WHERE cn.ID = #curID;
-- Use the template to generate a dynamic SQL statement specific to the current column:
SET #curSQL = REPLACE(#columnTestSQL, '<ColumnName>', #curColumnName);
-- Execute the dynamic SQL to check for overlap:
EXEC sp_executesql
#stmt = #curSQL,
#params = N'#hasOverlap_OUT BIT OUTPUT',
#hasOverlap_OUT = #curHasOverlap OUTPUT;
-- Record the results:
UPDATE #ColumnNames
SET HasOverlap = #curHasOverlap
WHERE ID = #curID;
SET #curID += 1;
END
-- Output a list of fields with no overlap:
SELECT ColumnName
FROM #ColumnNames
WHERE HasOverlap = 0;
I have to admit I was a little thrown off by the tags on this question at first. I thought I would go ahead and post this anyway in case it helps.
Hi I have data like this
What I want is a function giving 1 single cell value for for a given data of row= America and column = 100 the value should be 4. So far I have created a procedure not sure if its correct (to be able to use in asp.net) I think I need function. Here it is:
create proc [dbo].[proc2] #productname as varchar(20), #measurement as nvarchar (20)
as
begin
select a.*, b.column_name from pro a, INFORMATION_SCHEMA.COLUMNS b
where a.test=#productname and column_name=#measurement and b.table_name='pro'
end
exec dbo.proc2 'india',[120]
give me whole column.
Thank you for your help. Appreciate your time and guidance.
Sonu
With a stored procedure that gets the #ID_ROW and #POSITION_COLUMN parameteres :
CREATE TABLE #TEMP1
(
POSITION INT IDENTITY(1,1),
NAME SYSNAME
)
INSERT INTO #TEMP1 (NAME)
SELECT NAME FROM
SYSCOLUMNS
WHERE OBJECT_NAME(ID)= 'your table here'
SELECT #COLUMN_NAME = NAME FROM #TEMP1 WHERE POSITION=#POSITION_COLUMN
SELECT #COLUMN_NAME FROM PV3QTLOC023 WHERE
ROW = #ID_ROW