Select (Select field from FieldTable) from Table - sql-server

I'm using MSQL 2005. I have 2 table.A and B
Table A
- ID DOVKOD
- 1 KURSATIS
Table B
- ID KURALIS KURSATIS
- 1 2,2522 2,2685
- 2 2,4758 2,4874
Table A has only 1 record
When I execute Select (Select DOVKOD from Table A) from Table B I want to get same result as Select KURSATIS from Table B
I am gonna use it in a view. How can I do that. Thanks..

You can simply use a CASE expression:
SELECT CASE WHEN (SELECT DOVKOD FROM A) = 'KURSATIS' THEN KURSATIS
ELSE KURALIS
END
FROM B
SQL Fiddle Demo here

You must use Dynamic TSQL
SELECT #column=DOVKOD from Table A
EXEC ('Select ' + #column + ' from Table B')

If I understood you right then in table A you have the name of the column that you want to return. Then your solution is bad at all. I'll rather do something like that:
CREATE TABLE #TableA
(
ID INT, DOVKOD VARCHAR(100)
);
INSERT INTO #TableA VALUES (1, 'KURSATIS');
CREATE TABLE #TableB
(
ID INT, Value DECIMAL (18,2),Name VARCHAR(100)
);
INSERT INTO #TableB VALUES (1, 2.2522 , 'KURALIS');
INSERT INTO #TableB VALUES (2, 2.4758 , 'KURSATIS');
SELECT #TableB.* FROM #TableB JOIN #TableA ON #TableA.DOVKOD = #TableB.Name

The only way how to do this in MySQL is using Prepared statements. Dynamic pivot tables (transform rows to columns) is a good article about this.
SET #sql = NULL;
Select DOVKOD INTO #sql
FROM from Table A;
SET #sql = CONCAT('SELECT ', #sql, 'FROM Table B');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Related

SQL Server query items with IN query but keep order

I have to query specific order of string IDs example data:
| ID | RES |
---------------
| A_12 | 1.89 |
| B_27 | 4.53 |
| B_28 | 1.02 |
| C_23 | 2.67 |
A tool generated a specific order which does not follow any standard ordering rule, and I cannot change that order.
I am getting ~20000 of these rows and the RES is misaligned.
I'd like to make a simple query which would collect all needed records by a list IDs and would give me a custom defined ordered list of results.
Something like:
SELECT RES FROM TABLE1 WHERE ID IN ('A_12', 'C_23', 'B_28', 'B_27')
and I'd lke it to return
1.89
2.67
1.02
4.53
I understand IN query would not follow order as under the hood it most likely gets translated to (ID = A OR ID = B OR ID = C) query.
How do I enforce the result of the IN query to maintain my defined order? Do I need to create a temp table with one column for maintaining order? Any good solutions?
Use JOIN instead of using IN and explicitly specify your order:
DECLARE #Test TABLE (
ID VARCHAR(32),
RES DECIMAL(5,2)
)
INSERT #Test (ID, RES)
VALUES
('A_12', 1.89),
('B_27', 4.53),
('B_28', 3.54),
('C_23', 2.67)
SELECT t.ID, t.RES
FROM #Test t
JOIN (
VALUES
('A_12', 1),
('C_23', 2),
('B_28', 3),
('B_27', 4)
) o(ID, OrderId) ON t.ID = o.ID
ORDER BY o.OrderId
Instead of temp table you can use values where you specify the desired order in the additional column, like this:
declare #table1 table(id varchar(10), res decimal(10,2));
insert into #table1 (id, res)
values
('A_12', 1.89),
('B_27', 4.53),
('B_28', 3.54),
('C_23', 2.67);
select t.*
from #table1 t
join (values(1, 'A_12'), (2, 'C_23'), (3, 'B_28'), (4, 'B_27')) v(id,val)
on t.id = v.val
order by v.id;
#Table1 here is a substitute of your physical Table1.
There is no order to keep.
Returns of a select are NOT ORDERED by SQL basic definition, UNLESS YOU DEFINE AN ORDER.
So, there is no order to keep. Period.
If you want to keep one, use a temporary table / table variable for the valeus in IN (and obviously a join) and order by an order you also keep in a second field in said variable.
And no, this is not new - SQL is based on the SET theorem ever since Cobb published his famous paper back in the 1960s or so and never had order in returned results outside of side effects of implementation.
Do I need to create a temp table with one column for maintaining order
This seems to be working:
create table #tmp
(
CustomOrder int,
ID varchar(100)
)
insert into #tmp values (1, 'A_12')
insert into #tmp values (2, 'C_23')
insert into #tmp values (3, 'B_28')
insert into #tmp values (4, 'B_27')
query:
SELECT RES FROM TABLE1 INNER JOIN #tmp ON TABLE1.ID = #tmp.ID WHERE TABLE1.ID IN ('A_12', 'C_23', 'B_28', 'B_27')
ORDER BY #tmp.CustomOrder
output:
1.89
2.67
1.02
4.53
Any better and easier solution?
Just a different approach:
SELECT RES FROM TABLE1 WHERE ID IN ('A_12')
UNION ALL
SELECT RES FROM TABLE1 WHERE ID IN ('C_23')
UNION ALL
SELECT RES FROM TABLE1 WHERE ID IN ('B_28')
UNION ALL
SELECT RES FROM TABLE1 WHERE ID IN ('B_27')
I supposed that the JOIN option is more efficent than this approach. If you want to automatize this option:
DROP TABLE #TABLE1
CREATE TABLE #TABLE1(ID NVARCHAR(4), RES FLOAT)
INSERT INTO #TABLE1 VALUES('A_12',1.89)
INSERT INTO #TABLE1 VALUES('B_27',4.53)
INSERT INTO #TABLE1 VALUES('B_28',1.02)
INSERT INTO #TABLE1 VALUES('C_23',2.67)
DECLARE #ID TABLE(ID NVARCHAR(4) not null);
--HERE HAVE TO INSERT IN ORDER YOU WANT TO RETURN THE RESULTS IN THE QUERY
insert into #ID VALUES('A_12')
insert into #ID VALUES('B_27')
insert into #ID VALUES('B_28')
insert into #ID VALUES('C_23')
DECLARE #UNIONALL NVARCHAR(10) = CHAR(13) + N'UNION ALL'
DECLARE #QUERY NVARCHAR(MAX) = NULL
DECLARE #ID_SEARCH NVARCHAR(4) = NULL
DECLARE C CURSOR FAST_FORWARD FOR SELECT ID FROM #ID
OPEN C
FETCH NEXT FROM C INTO #ID_SEARCH
SET #QUERY = N'SELECT RES FROM #TABLE1 WHERE ID = ''' + #ID_SEARCH + ''' '
FETCH NEXT FROM C INTO #ID_SEARCH
WHILE ##FETCH_STATUS = 0 BEGIN
SET #QUERY = #QUERY + #UNIONALL
SET #QUERY = #QUERY + N' SELECT RES FROM #TABLE1 WHERE ID = ''' + #ID_SEARCH + ''' '
FETCH NEXT FROM C INTO #ID_SEARCH
END
EXECUTE master..sp_executesql #QUERY

Based on a variable: SQL Query to fetch data for multiple values from one column OR get all values from that column

Problem Statement :
when #a has a single word(Ex. 'name1') OR comma separated string (Example 'name1,name2,name3') then the query should return the manager names of employees with name1 and name2 and name3
when #a has an empty string then return the manager names of all the employees in the emp_master table
I have defined a stored procedure where I pass a variable.
This variable can be a comma separated string, a single word or an empty string.
If the string is comma separated then I split that string and get values based on the return table of split statement
else
I get the related value of the non comma separated data using normal subquery
I have tried to achieve this in the following way
Declare #a varchar(50)= ''
select emp.Name from
emp_master emp
where
(LEN(#a)=0 AND emp.Name in
(
SELECT DISTINCT [Name] FROM
[dbo].[Emp_Master] WHERE [EmpId] IN
(
SELECT
DISTINCT [MGR_ID]
FROM [dbo].[Emp_Master]
)
)
)
OR
emp.Name in (Select * from [dbo].[SplitString](#a, ','))
Details for the above sample:
[dbo].[SplitString] - custom written function : returns a table of split values. So
Select * from [dbo].SplitString
will return
SplitTable
----------
name1
name2
name3
and
Select * from [dbo].[SplitString](',','name1')
will return
SplitTable
----------
name1
[dbo].[Emp_Master] contains data for all the employees
[MGR_ID] is the column which has the employeeID of the employee manager
#a is the input variable
The Database is MS SQL 2008
My current solution(the above insane query) solves my purpose but it is very slow, it would be helpful to get an optimized and faster working solution for the problem
Emp_master Table has 400 000 rows, 30 columns
There are 18 000 managers in that table
CREATE NONCLUSTERED INDEX ix ON dbo.Emp_Master ([MGR_ID])
GO
DECLARE #a VARCHAR(50) = ''
DECLARE #t TABLE (val VARCHAR(50) PRIMARY KEY WITH(IGNORE_DUP_KEY=ON))
INSERT INTO #t
SELECT item = t.c.value('.', 'INT')
FROM (
SELECT txml = CAST('<r>' + REPLACE(#a, ',', '</r><r>') + '</r>' AS XML)
) r
CROSS APPLY txml.nodes('/r') t(c)
SELECT /*DISTINCT*/ [Name]
FROM dbo.Emp_Master e1
WHERE (
#a = ''
AND
e1.[EmpId] IN (SELECT DISTINCT MGR_ID FROM dbo.Emp_Master)
)
OR (
#a != ''
AND
e.Name IN (SELECT * FROM #t)
)
OPTION(RECOMPILE)
TRY THIS
CREATE NONCLUSTERED INDEX IX_MGR_ID_Emp_Master ON dbo.Emp_Master ([MGR_ID])
GO
Create Procedure searchname (#a varchar(255))
as
IF (#a = '')
BEGIN
EXEC Searchname1 #a
END
ELSE
BEGIN
EXEC Searchname2 #a
END
GO
Create Procedure Searchname1 (#a varchar(255))
AS
SELECT DISTINCT [Name] FROM
[dbo].[Emp_Master] m1 WHERE
exists
(
SELECT
*
FROM [dbo].[Emp_Master] m2
WHERE
m1.[EmpId]= m2.[MGR_ID]
)
GO
Create Procedure Searchname2 (#a varchar(max))
AS
Select #a = ' SELECT '''+replace( #a,',',''' Union ALL SELECT ''')+' '''
Create table #names (name varchar(255))
insert into #names
EXEC ( #a )
select emp.Name from
emp_master emp
WHERE
emp.Name in( Select name FRom #names)
option (recompile)
IF YOU ARE ALREADY DEALING WITH SQL INJECTION AT APPLICATION LEVEL
THEN
ALTER procedure [dbo].[Searchname2] (#a varchar(max))
AS
select #a = ''''+replace ( #a,',',''',''')+''''
DECLARE #sql NVARCHAR(MAX) = N'
select distinct emp.Name from
emp_master emp
WHERE
emp.Name in( '+#a+')'
EXEC (#sql)

How to call a recursive function in sql server

I have a table as follows
cat_id Cat_Name Main_Cat_Id
1 veg null
2 main course 1
3 starter 1
4 Indian 2
5 mexican 2
6 tahi 3
7 chinese 3
8 nonveg null
9 main course 8
10 indian 9
11 starter 8
12 tahi 11
13 chinese 11
(Main_Cat_Id is cat_id of previously added category in which it belongs)
This table is used for the categories the product where veg category has the two sub category main course and starter which is identify by main_cat_id
and those subcategories again has sub category as indian and mexican
And this categorization is dependent on the user; he can add more sub categories to indian, mexican also so that he can have any level of categorization
now I have to select all the subcategories of any node like if I take veg i have to select
(1)veg > (2)main course(1) > (4)indian(2)
> (5)mexican(2)
> (3)starter(1) > (6)thai(3)
> (7)chinese(3)
to form the string as 1,2,4,5,3,6,7
to do this i wrote a sql function as
CREATE FUNCTION [dbo].[GetSubCategory_TEST]
( #MainCategory int, #Category varchar(max))
RETURNS varchar(max)
AS
BEGIN
IF EXISTS (SELECT Cat_Id FROM Category WHERE Main_Cat_Id=#MainCategory)
BEGIN
DECLARE #TEMP TABLE
(
CAT_ID INT
)
INSERT INTO #TEMP(CAT_ID) SELECT Cat_Id FROM Category WHERE Main_Cat_Id=#MainCategory
DECLARE #TEMP_CAT_ID INT
DECLARE CUR_CAT_ID CURSOR FOR SELECT CAT_ID FROM #TEMP
OPEN CUR_CAT_ID
WHILE 1 =1
BEGIN
FETCH NEXT FROM CUR_CAT_ID
INTO #TEMP_CAT_ID;
IF ##FETCH_STATUS <> 0
SET #Category=#Category+','+ CONVERT(VARCHAR(50), #TEMP_CAT_ID)
SET #Category = [dbo].[GetSubCategory](#TEMP_CAT_ID,#Category)
END
CLOSE CUR_CAT_ID
DEALLOCATE CUR_CAT_ID
END
return #Category
END
but this function keep on executing and not gives the desired output i don't understands what wrong is going on plz help me to get this
You dont need a recursive function to build this, you can use a Recursive CTE for that.
Something like
DECLARE #TABLE TABLE(
cat_id INT,
Cat_Name VARCHAR(50),
Main_Cat_Id INT
)
INSERT INTO #TABLE SELECT 1,'veg',null
INSERT INTO #TABLE SELECT 2,'main course',1
INSERT INTO #TABLE SELECT 3,'starter',1
INSERT INTO #TABLE SELECT 4,'Indian',2
INSERT INTO #TABLE SELECT 5,'mexican',2
INSERT INTO #TABLE SELECT 6,'tahi',3
INSERT INTO #TABLE SELECT 7,'chinese',3
INSERT INTO #TABLE SELECT 8,'nonveg',null
INSERT INTO #TABLE SELECT 9,'main course',8
INSERT INTO #TABLE SELECT 10,'indian',9
INSERT INTO #TABLE SELECT 11,'starter',8
INSERT INTO #TABLE SELECT 12,'tahi',11
INSERT INTO #TABLE SELECT 13,'chinese',11
;WITH Recursives AS (
SELECT *,
CAST(cat_id AS VARCHAR(MAX)) + '\' ID_Path
FROM #TABLE
WHERE Main_Cat_Id IS NULL
UNION ALL
SELECT t.*,
r.ID_Path + CAST(t.cat_id AS VARCHAR(MAX)) + '\'
FROM #TABLE t INNER JOIN
Recursives r ON t.Main_Cat_Id = r.cat_id
)
SELECT *
FROM Recursives
I am ashamed, but I used #astander scipt to give string result.
First I created data you gave.
Second I collect rows which I need
And then using XML I put everything in one row (function STUFF removes first comma)
DECLARE #TABLE TABLE(
cat_id INT,
Cat_Name VARCHAR(50),
Main_Cat_Id INT
)
DECLARE #Collected TABLE(
cat_id INT
)
INSERT INTO #TABLE SELECT 1,'veg',null
INSERT INTO #TABLE SELECT 2,'main course',1
INSERT INTO #TABLE SELECT 3,'starter',1
INSERT INTO #TABLE SELECT 4,'Indian',2
INSERT INTO #TABLE SELECT 5,'mexican',2
INSERT INTO #TABLE SELECT 6,'tahi',3
INSERT INTO #TABLE SELECT 7,'chinese',3
INSERT INTO #TABLE SELECT 8,'nonveg',null
INSERT INTO #TABLE SELECT 9,'main course',8
INSERT INTO #TABLE SELECT 10,'indian',9
INSERT INTO #TABLE SELECT 11,'starter',8
INSERT INTO #TABLE SELECT 12,'tahi',11
INSERT INTO #TABLE SELECT 13,'chinese',11
INSERT INTO #TABLE SELECT 14,'chinese',6
DECLARE #nodeID INT = 1;
DECLARE #result VARCHAR(MAX);
;WITH Recursives AS (
SELECT cat_id, main_cat_id
FROM #TABLE
WHERE Cat_Id = #nodeID
UNION ALL
SELECT T.cat_id, T.main_cat_id
FROM #TABLE AS T
INNER JOIN Recursives AS R
ON t.Main_Cat_Id = r.cat_id
)
INSERT INTO #Collected
SELECT cat_id
FROM Recursives
SELECT #result = STUFF(
(SELECT ',' + CAST( cat_id AS VARCHAR)
FROM #Collected
ORDER BY cat_id
FOR XML PATH('')
), 1,1,'')
SELECT #result
Your cursor is looping infinitely because you asked it to keep going until 1 no longer equals 1:
WHILE 1 =1
1=1 is always true so the loop never ends, and you don't explicitly break out of it anywhere.
You would do well to study some examples of cursors, for example this one in the Microsoft T-SQL documentation. They are quite formulaic and the main syntax rarely needs to vary much.
The standard approach after opening the cursor is to do an initial fetch next to get the first result, then open a while loop conditional on ##FETCH_STATUS = 0 (0 meaning successful).
Because you're looking only for unsuccessful cursor fetch states inside your cursor:
IF ##FETCH_STATUS <> 0
The setting of #Category will only happen once the cursor has gone past the last row in the set. I suspect this is exactly what you don't want.
I'm also not sure about the scoping of the #Category variable, since it's an input parameter to the function; I generally create new variables inside a function to work with, but off the top of my head I'm not sure this will actually create a problem or not.
More generally, although I don't totally understand what you're trying to achieve here, a recursive function involving a cursor is probably not the right way to do it, as Adriaan Stander's answer suggests.

Converting arithmetic formula in a string into a values

I have a table in which all entries are in form of arithmetic formulas (i.e. 1+2+3 etc).
In this table all columns are of type varchar. The table has many columns like this.
I want to calculate formula and insert the values into another. Any suggestions on how to achieve this?
You can try this. I hope this can assist you. It takes expression from t1 and put the result in t2. You won't want to make t1 the actual table as it deletes from the table, so you can copy the actual table value to a temp table
declare #t as table(val varchar(20))
declare #t2 as table(val varchar(20))
insert into #t values
('1+3'),
('2*3'),
('9+3*2')
declare #exp varchar(20)
while(exists(select 1 from #t))
begin
select top(1) #exp = val from #t
insert into #t2
exec ('select '+#exp)
delete top (1) from #t
end
select * from #t2
Result
val
------
4
6
15
You can check these links Similar question here and Convert string with expression to decimal
Follow the link -
Convert string with expression to decimal
After you create function DBO.EVAL , just Run :-
select dbo.eval(column_name) from table_name;
You can declare two table variables:
#tabformula in which will be stored the formulas
#tabresult in which will stored the results
There is an ID column that will be used for joining the two tables.
After filling the first table with the required formulas, you declare a cursor, and then you execute the formula and store the result into the #tabresult table.
declare #tabformula as table (id int identity(1,1), formula
varchar(200)) declare #tabresult as table (id int, result int)
insert into #tabformula(formula) values('1+4+6+7') ;
insert into #tabformula(formula) values('10+4+60+7');
insert into #tabformula(formula) values('1+4+6+70') ;
insert into #tabformula(formula) values('1+44+65+7');
declare c cursor for select ID,formula from #tabformula declare #id as
int declare #formula as varchar(200)
open c fetch c into #id,#formula while ##fetch_status=0 begin print
#formula insert into #tabresult (id,result) exec( 'select '+ #id +
','+#formula ) fetch c into #id,#formula end close c deallocate c
select T1.id,t1.formula,T2.result from #tabformula t1 inner join
#tabresult t2 on t1.id=t2.id

How to store a multiple or a list of values returned from sp_executesql?

UPDATE : This is what I did -
set #dyn_sql = '
select
#UserName=UserName
from
(
select
E.ID as EmployeeID,
E.UserName as Username
from
Leaderboard K
inner join Employee E on K.EmployeeId = E.Id
inner join INFO KD on KD.EmployeeId=E.Id
where E.CompanyId=4
) as d1'
DECLARE #leaderboards TABLE
(
UserName varchar(50)
)
set #params='#Employee_Id int, #UserName varchar(200) OUTPUT'
INSERT INTO #leaderboards (UserName)
EXEC sp_executesql #dyn_sql, #params,#EmployeeId=#Employee_Id OUTPUT,#UserName = #User_Name OUTPUT
SELECT * from #leaderboards
But this is not returning records although if I see the query is right and returns records..
Hi all, I am executing a dynamic sql statement using sp_executesql and this is what I am doing currently -
EXEC sp_executesql #dyn_sql, #params,#EmployeeId=#Employee_Id OUTPUT,#UserName = #User_Name OUTPUT
SELECT #Employee_Id AS EmployeeId,#User_Name AS UserName
But the above only gets me single value when I am getting a list of records if I run the dynamic sql query individually.How can I store the list of values returned by execution of my dynamic sql ?
and returns records..and returns records..But this
Via a temp table.
[Update]
declare #sql nvarchar(max)
set #sql = '
select
E.ID as EmployeeID, --doesn't really matter how you name them, it's the order that matters
E.UserName as Username -- and this order should match the order of columns in the insert statement
from
Leaderboard K
inner join Employee E on K.EmployeeId = E.Id
inner join INFO KD on KD.EmployeeId=E.Id
where E.CompanyId=4
'
DECLARE #LeaderBoard TABLE
(
EmployeeId int,
UserName varchar(50)
)
INSERT INTO #LeaderBoard (EmployeeId, UserName)
exec sp_executesql #sql
select * from #LeaderBoard
--Hurray, we made it!
You can insert the results of EXEC sp_executesql into a temp table or table variable.
DECLARE #t TABLE
(
a INT,
b INT
)
INSERT INTO #t (a,b)
EXEC sp_executesql N'SELECT 1, 2 UNION SELECT 3, 4 '
Or alternatively the dynamic SQL can access a temp table declared in the parent scope (but any temp table created in the dynamic SQL itself will be out of scope as soon as the execution finishes)

Resources