I have a query that returns a reasonable number of records from a table. I need to include a comma delimited string as an output column. Something like this
SELECT
column1,
column2,
column3,
[Delimited string based on the id]
FROM
sometable
WHERE
id = someid
I know you can use the coalesce function which i have used in the past but im not sure how to integrate it into a select statement, also not really sure on the performance?
Any ideas?
I'd loop through the items to build the string with, then add it to the result set.
-- Minor performance tweak.
SET NOCOUNT ON
GO
-- ID of the item or widget or whatever
DECLARE #someid int
DECLARE #LookupItems TABLE
(
ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
LookupTableID int, -- Primary key of the lookup table.
LookupField varchar(30) -- Text of the lookup value.
)
-- Update to the desired id.
SET #someid = 1234
INSERT INTO #LookupItems (ID, LookupField)
SELECT ID, Value
FROM dbo.somelookuptable
WHERE ID IN (
SELECT lookupid
FROM dbo.sometable
WHERE ID = #someid
)
DECLARE
#Count int,
#Max int,
#DelimitedString varchar(1000)
-- Initialize with a non-NULL value to facilitate concatenation.
SET #DelimitedString = ''
SET #Count = 1
SELECT #Max = MAX(ID) FROM #LookupItems
WHILE (#Count <= #Max)
BEGIN
SELECT #DelimitedString = #DelimitedString + IsNull(somefield, '') + ','
FROM #LookupItems
WHERE ID = #Count
SET #Count = #Count + 1
END
SELECT
column1,
column2,
column3,
#DelimitedString
FROM dbo.sometable
WHERE id = someid
Related
I'm using SQL server 2014 I try to get row that have multi-value in name like:
declare #value ='m n'
So the value that return should have 'm' and 'n' in any position
I tried to use
Select * from Table where contains(name,N'"*m*" and "*n*"')
But the value return only if it started by 'm' and 'n'
I had also tried:
select * from Table where name like '%m%n%'
the value return only if name contains 'm' then 'n' not 'n' then 'm'.
Note: I'm getting value from parameter so I don't known how many char or what position so I'm using replace on #value to get what I want.
Since you want to use that variable.
Then best split the letters or words.
Having the STRING_SPLIT function would be great for this.
But there are more ways to split strings.
The example below does it with a WHILE loop.
CREATE TABLE [Table]
( id INT IDENTITY(101,1) PRIMARY KEY,
name NVARCHAR(100) NOT NULL
);
GO
✓
INSERT INTO [Table] (name) VALUES
('a munchkin'),
('never ever sever mah lever'),
('saintess'),
('them mammaries');
GO
4 rows affected
DECLARE #value NVARCHAR(1000);
SET #value =' m n ';
DECLARE #values TABLE (
value NVARCHAR(42)
);
DECLARE #words NVARCHAR(1000);
DECLARE #word NVARCHAR(42);
SET #words = RTRIM(LTRIM(#value))+' ';
WHILE CHARINDEX(' ', #words) > 0
BEGIN
SET #word = SUBSTRING(#words,0,CHARINDEX(' ',#words))
SET #words = LTRIM(SUBSTRING(#words,CHARINDEX(' ',#words)+1,LEN(#words)))
IF #word != '' INSERT INTO #values (value) VALUES (#word);
END;
DECLARE #TotalValues INT;
SET #TotalValues = (select count(distinct value) from #values);
--
-- use the table variable to query the table
--
SELECT *
FROM [Table] t
WHERE EXISTS
(
SELECT 1
FROM #values v
WHERE t.name LIKE '%'+v.value+'%'
HAVING COUNT(DISTINCT v.value) = #TotalValues
);
GO
id | name
--: | :-------------------------
101 | a munchkin
102 | never ever sever mah lever
db<>fiddle here
You have to separate your conditions like this
select * from Table where name like '%m%' and name like '%n%'
I created a temp table #test containing 3 fields: ColumnName, TableName, and Id.
I would like to see which rows in the #test table (columns in their respective tables) are not empty? I.e., for every column name that i have in the ColumnName field, and for the corresponding table found in the TableName field, i would like to see whether the column is empty or not. Tried some things (see below) but didn't get anywhere. Help, please.
declare #LoopCounter INT = 1, #maxloopcounter int, #test varchar(100),
#test2 varchar(100), #check int
set #maxloopcounter = (select count(TableName) from #test)
while #LoopCounter <= #maxloopcounter
begin
DECLARE #PropIDs TABLE (tablename varchar(max), id int )
Insert into #PropIDs (tablename, id)
SELECT [tableName], id FROM #test
where id = #LoopCounter
set #test2 = (select columnname from #test where id = #LoopCounter)
declare #sss varchar(max)
set #sss = (select tablename from #PropIDs where id = #LoopCounter)
set #check = (select count(#test2)
from (select tablename
from #PropIDs
where id = #LoopCounter) A
)
print #test2
print #sss
print #check
set #LoopCounter = #LoopCounter + 1
end
In order to use variables as column names and table names in your #Check= query, you will need to use Dynamic SQL.
There is most likely a better way to do this but I cant think of one off hand. Here is what I would do.
Use the select and declare a cursor rather than a while loop as you have it. That way you dont have to count on sequential id's. The cursor would fetch fields columnname, id and tablename
In the loop build a dynamic sql statement
Set #Sql = 'Select Count(*) Cnt Into #Temp2 From ' + TableName + ' Where ' + #columnname + ' Is not null And ' + #columnname <> '''''
Exec(#Sql)
Then check #Temp2 for a value greater than 0 and if this is what you desire you can use the #id that was fetched to update your #Temp table. Putting the result into a scalar variable rather than a temp table would be preferred but cant remember the best way to do that and using a temp table allows you to use an update join so it would well in my opinion.
https://www.mssqltips.com/sqlservertip/1599/sql-server-cursor-example/
http://www.sommarskog.se/dynamic_sql.html
Found a way to extract all non-empty tables from the schema, then just joined with the initial temp table that I had created.
select A.tablename, B.[row_count]
from (select * from #test) A
left join
(SELECT r.table_name, r.row_count, r.[object_id]
FROM sys.tables t
INNER JOIN (
SELECT OBJECT_NAME(s.[object_id]) table_name, SUM(s.row_count) row_count, s.[object_id]
FROM sys.dm_db_partition_stats s
WHERE s.index_id in (0,1)
GROUP BY s.[object_id]
) r on t.[object_id] = r.[object_id]
WHERE r.row_count > 0 ) B
on A.[TableName] = B.[table_name]
WHERE ROW_COUNT > 0
order by b.row_count desc
How about this one - bitmask computed column checks for NULLability. Value in the bitmask tells you if a column is NULL or not. Counting base 2.
CREATE TABLE FindNullComputedMask
(ID int
,val int
,valstr varchar(3)
,NotEmpty as
CASE WHEN ID IS NULL THEN 0 ELSE 1 END
|
CASE WHEN val IS NULL THEN 0 ELSE 2 END
|
CASE WHEN valstr IS NULL THEN 0 ELSE 4 END
)
INSERT FindNullComputedMask
SELECT 1,1,NULL
INSERT FindNullComputedMask
SELECT NULL,2,NULL
INSERT FindNullComputedMask
SELECT 2,NULL, NULL
INSERT FindNullComputedMask
SELECT 3,3,3
SELECT *
FROM FindNullComputedMask
I want query field with different alias in stored procedure
select COUNT(EmpCode) as CountEmp+#para
result shoud be
CountEmp1
45
CountEmp2
54
CountEmp1
76
Query loop in c# code:
select COUNT(EmpCode) where something = #something as CountEmp+#para
Approach without dynamic SQL:
--I create temp table for demonstration
DECLARE #some_table TABLE (
Something int,
EmpCode INT
)
INSERT INTO #some_table (Something, EmpCode)
VALUES (1, 10),(1, 22),(1, 12),(2, 12),(2, 30),(3, 65),(3, 15),(3, 11),(3, 5)
--Declare parameter we want to search
DECLARE #param int = 1
--Query
--In cte we select what we need based on parameter
;WITH cte AS (
SELECT 'CountEmp'+CAST(#param as nvarchar(10)) as SomeThing,
CAST(COUNT(EmpCode) as nvarchar(10)) as EmpCodeCount,
ROW_NUMBER() OVER (ORDER BY SomeThing ) as rn
FROM #some_table
WHERE SomeThing = #param
GROUP BY SomeThing
)
--And here comes UNION
SELECT SomeThing as Result
FROM (
SELECT SomeThing,rn
FROM cte
UNION ALL
SELECT EmpCodeCount ,rn
FROM cte
) as t
ORDER BY rn, SomeThing DESC
Output:
Result
------------------
CountEmp1
3
(2 row(s) affected)
Please try to make use of below code. Its working fine with SQL Server 2012.
IF OBJECT_ID ('temp..#Mytable') IS NOT NULL
CREATE TABLE #Mytable (ID INT IDENTITY (1,1),EmpCode INT)
DECLARE #max int ,#count int
SET #max =0;
DECLARE #str varchar(10)
INSERT #Mytable
(EmpCode)
VALUES
(10),
(45),
(35),
(63),
(56),
(65)
SET #count = (SELECT COUNT (ID) FROM #Mytable )
WHILE #count > #max
BEGIN
SET #max = #max+1
SET #str = CONVERT(varchar(10),#max)
EXEC('SELECT EmpCode AS Empcode'+#str+ ' FROM #Mytable WHERE ID = '+#str)
END
Consider the table in SQL Server 2012
789-0000000
The above number will be consider as a string in SQL Server 2012, but whenever I update the record I need increment to 1.
For example:
When I update the record 1 it should increment to 789-0000001
When I update the record 2 it should increment to 789-0000002
Finally increment should done only 789-0000000
The best solution is to use
an ID INT IDENTITY(1,1) column to get SQL Server to handle the automatic increment of your numeric value
a computed, persisted column to convert that numeric value to the value you need
So try this:
CREATE TABLE dbo.YourTable
(ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
CompanyID AS '789-' + RIGHT('000000' + CAST(ID AS VARCHAR(7)), 7) PERSISTED,
.... your other columns here....
)
Now, every time you insert a row into dbo.YourTable without specifying values for ID or CompanyID:
INSERT INTO dbo.YourTable(Col1, Col2, ..., ColN)
VALUES (Val1, Val2, ....., ValN)
then SQL Server will automatically and safely increase your ID value, and CompanyID will contain values like 789-0000001, 789-0000002,...... and so on - automatically, safely, reliably, no duplicates.
DECLARE #base int = 0
UPDATE TableX
SET
TableX.Value = 'Prefix' + RIGHT('0000000' + CAST(#base AS nvarchar),7),
#base = #base + 1
FROM
TableX
you can split the string
e.g.:
SELECT Item
FROM dbo.SplitString('Apple,Mango,Banana,Guava', ',')
then cast it
e.g.:
SELECT CAST(YourVarcharCol AS INT) FROM Table
then manually increment it
e.g.:
DECLARE max_id INT
SET #max_id = (SELECT MAX(id) FROM source_table)
DECLARE cursor_name CURSOR FOR
SELECT columns, to, copy
FROM source_table
OPEN cursor_name
FETCH NEXT FROM cursor_name
INTO #columns, #to, #cop
at update
e.g.:
declare #i int = SELECT ISNULL(MAX(interfaceID),0) + 1 FROM prices
update prices
set interfaceID = #i , #i = #i + 1
where interfaceID is null
you can understand how complicate this is and why
the solution using a constant to store that prefix is right one.
Declare #str varchar(max) = '789-0000000'
Select
SUBSTRING ( #str ,0 ,CHARINDEX ( '-' ,#str ))
+'-'
+
(SUBSTRING ( #str ,(CHARINDEX ( '-' ,#str)+1) ,(7-LEN(CAST(SUBSTRING ( #str ,CHARINDEX ( '-' ,#str)+1,LEN(#str)) as int))
)
)+
CAST(CAST(SUBSTRING ( #str ,CHARINDEX ( '-' ,#str)+1,LEN(#str)) as int)+1 as varchar))
When #str='789-0001947'
Output #str= 789-0001948
You can write a update trigger on the table with above logic.
I have a procedure with a (slightly more complex) version of the below:
CREATE PROC sp_Find_ID (
#Match1 varchar(10),
#Match2 varchar(10)
) AS
DECLARE #ID int
SELECT #ID = ID
FROM Table1
WHERE Match1 = #Match1
AND Coalesce(Match2,#Match2,'') = Coalesce(#Match2,Match2,'')
SELECT #ID ID
Essentially Match1 is a mandatory match, but Match2 is both optional on the input to the procedure, and on the table being searched. The 2nd match succeeds where the input and/or the table Match2 values are null, or where they're both the same (not null) value.
My question is: Is there a more efficient (or even more readable) way of doing this?
I've used this method a few times, and I feel slightly sullied each time (subjective dirtiness admittedly).
Is there a more efficient (or even more readable) way of doing this?
The example you provided, using COALESCE/etc is non-sargable. You need to separate things so only what needs to be present in the query is run:
DECLARE #ID int
IF #Match2 IS NOT NULL
BEGIN
SELECT #ID = t.id
FROM TABLE1 t
WHERE t.match1 = #Match1
AND (t.match2 = #Match2 OR t.match2 IS NULL)
END
ELSE
BEGIN
SELECT #ID = t.id
FROM TABLE1 t
WHERE t.match1 = #Match1
END
SELECT #ID ID
If you want this to occur in a single SQL statement, dynamic SQL is the only real alternative. I highly recommend reading The curse and blessing of dynamic SQL before reading further:
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N' SELECT #ID = t.id
FROM TABLE1 t
WHERE t.match1 = #Match1 '
SET #SQL = #SQL + CASE
WHEN #Match2 IS NOT NULL THEN
' AND (t.match2 = #Match2 OR t.match2 IS NULL) '
ELSE
' '
END
BEGIN
EXEC sp_executesql #SQL,
N'#ID INT OUTPUT, #Match1 VARCHAR(10), #Match2 VARCHAR(10)',
#ID, #Match1, #Match2
END
Avoiding OR and ISNULL etc
The EXCEPT bit returns no rows if either side IS NULL
Match2 <> #Match2 means exclude non-NULL non-matching
Something like this
DROP TABLE dbo.Table1
CREATE TABLE dbo.Table1 (ID int NOT NULL, Match1 int NOT NULL, Match2 int NULL)
INSERT dbo.Table1 VALUES (1, 55, 99), (2, 55, NULL)
DECLARE #Match1 int = 55, #Match2 int
SELECT ID
FROM
(
SELECT ID FROM Table1 WHERE Match1 = #Match1
EXCEPT -- #Match2 = NULL, match both rows (99, NULL)
SELECT ID FROM Table1 WHERE Match2 <> #Match2
) foo
SET #Match2 = -1
SELECT ID
FROM
(
SELECT ID FROM Table1 WHERE Match1 = #Match1
EXCEPT -- #Match2 = -1, match ID = 2 only where Match2 IS NULL
SELECT ID FROM Table1 WHERE Match2 <> #Match2
) foo
Don't know if this is any more preferable.
SELECT #ID = ID
FROM Table1
WHERE Match1 = #Match1
AND ((Match2 = #Match2) OR Coalesce(Match2,#Match2) IS NULL)
I would have thought this should do it - providing that the #Match2 value will be NULL if it is optional.
CREATE PROC sp_Find_ID (
#Match1 varchar(10),
#Match2 varchar(10)
) AS
DECLARE #ID int
SELECT #ID = ID
FROM Table1
WHERE Match1 = #Match1
AND Match2 = IsNull(#Match2, Match2)
SELECT #ID ID
Seems simple to me? I must be missing something.. you dont need the Coalesce
SELECT #ID = ID
FROM Table1
WHERE Match1 = #Match1
AND (
(Match2 is null and #Match2 is null)
or
#Match2=Match2
)
SELECT #ID ID