Case expression in where clause - sql-server

I would like to get all records (6 rows) from testTable if a = 4. 4 is default parameter in SP.
create table testTable(a int, b int, c int)
go
insert into testTable values(2, 101, 100000)
go
insert into testTable values(2, 101, 100001)
go
insert into testTable values(3, 101, 100002)
go
insert into testTable values(3, 102, 100003)
go
insert into testTable values(4, 1, 100004)
go
insert into testTable values(4, 1, 100005)
go
create proc SPtest
#a int = 4,
#b int = 1
as
select * from testTable where a = #a and b = #b
exec SPtest 2, 101
Above works well. but I need something like this:
declare #a int
set #a = 4
select *
from testTable
where a = case #a when 4 then select distinct a from testTable end

There are few ways to cut the cookie, this would seem the most logical;
IF #a = 4 THEN
BEGIN
SELECT *
FROM testTable
END
ELSE
BEGIN
SELECT *
FROM testTable
WHERE a = #a and b = #b
END
Alternatively you can use an or statement;
SELECT *
FROM testTable
WHERE #a = 4 or (a = #a and b = #b)
Good luck (would have commented but I not there yet).
Regards,

Please try this:
create proc SPtest
#a int = 4,
#b int = 1
as
if #a = 4
select distinct a from testTable
else
select * from testTable where a = #a and b = #b
go
exec SPtest
exec SPtest 3,101

Related

How can I execute saved query statement in table in SQL Server

INSERT #Table
SELECT 'A','DECLARE #C INT = 1 SELECT #C+1'
UNION
SELECT 'B','DECLARE #C INT = 5 SELECT #C+6'
SELECT *
FROM #Table
I think you want to run column B query..
declare #Table as table(a varchar(100),b varchar(500))
INSERT #Table
SELECT 'A','DECLARE #C INT = 1 SELECT #C+1'
UNION
SELECT 'B','DECLARE #C INT = 5 SELECT #C+6'
DECLARE #VAR VARCHAR(500)
SET #VAR=(SELECT B FROM #Table WHERE A='A')
PRINT #VAR
EXEC (#VAR)
So you are trying to store the queries in your table and then dynamically call them based on some conditions. then you would want to execute those queries. the below code must work.
create table #table (
rowcnt int identity (1,1),
value1 varchar(10),
query1 varchar(8000))
declare #query varchar(8000);
INSERT #Table
SELECT 'A','DECLARE #C INT = 1 SELECT #C+1'
UNION
SELECT 'B','DECLARE #C INT = 5 SELECT #C+6'
select #query = query1 from #table where id =1
exec (#query)
Please note storing scripts in tables and then calling them is not a good practices.
As if the Stored procedure which will call this scripts ever hit any problems it would be a nightmare for debugging it. Also it would be tough for performing performance optimization on DB.
Note if you would need to concat multiple rows for getting the full query use the below code
select #query = COALESCE(#query,'') +query1 from #table where id =1

How can I get a missing value from "where in ()" in T-SQL?

If I have a table with a column named "Ids" and another column named "clients" and I have the following data:
IDs Clients
----------------
1 A
2 B
3 C
4 D
I'm trying to get the IDs for multiple clients without writing a query for each one.
So I am using:
select ids
from table1
where clients in (A,B,E,C)
This returns
1,2,3
But I need to know that E is missing. So I really need
1,2,NULL,3
How can I accomplish this?
You can inline the values
declare #t table (id int, client char(1))
insert into #t values
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D');
select *
from #t;
SELECT q.client, t.id
FROM ( values ('A'), ('B'), ('E') ) q(client)
left join #t t
on t.client = q.client;
Don't use plural for columns. A row is singular.
Insert your input to a table and proceed your query as below
declare #tbl table (ids int, clients varchar(10))
insert into #tbl values
(1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
SELECT * FROM #tbl
declare #value table (Val varchar(10))
insert into #value values
('A')
,('B')
,('C')
,('E')
select ids,t1.Val
from #tbl t
RIGHT JOIN #value t1 on t1.Val = t.clients
Alertnate option.
declare #tbl table (ids int, clients varchar(10))
insert into #tbl values
(1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
For SQL Server 2016 and above
Declare #var varchar(50) = 'A,B,C,E'
SELECT ids, t1.value
FROM #tbl t
RIGHT JOIN STRING_SPLIT(#var, ',') t1 on t1.value = t.clients
For lower than SQL Server 2016 use below query. You have to create strparse function. script given below
SELECT ids, t1.Keys
FROM #tbl t
RIGHT JOIN dbo.StrParse (',', #var) t1 on t1.Keys = t.clients
go
Create the function for parsing string to rows
CREATE FUNCTION [dbo].[StrParse]
(#delimiter CHAR(1),
#csv NTEXT)
RETURNS #tbl TABLE(Keys NVARCHAR(255))
AS
BEGIN
DECLARE #len INT
SET #len = Datalength(#csv)
IF NOT #len > 0
RETURN
DECLARE #l INT
DECLARE #m INT
SET #l = 0
SET #m = 0
DECLARE #s VARCHAR(255)
DECLARE #slen INT
WHILE #l <= #len
BEGIN
SET #l = #m + 1--current position
SET #m = Charindex(#delimiter,Substring(#csv,#l + 1,255))
IF #m <> 0
SET #m = #m + #l
--insert #tbl(keys) values(#m)
SELECT #slen = CASE
WHEN #m = 0 THEN 255
ELSE #m - #l
END
IF #slen > 0
BEGIN
SET #s = Substring(#csv,#l,#slen)
INSERT INTO #tbl
(Keys)
SELECT #s
END
SELECT #l = CASE
WHEN #m = 0 THEN #len + 1
ELSE #m + 1
END
END
RETURN
END

Concatenating Value in Update

Ok, I have been banging my head against the wall for about 20 minutes and I can't seem to figure this one out. I have two tables each with a common field (ID) and what I want to do is to concatenate the values form #T2's UDValue column into #T1's UDValue column
CREATE TABLE #T1(ID INT, UDValue NVARCHAR(50))
CREATE TABLE #T2(ID INT, UDValue NVARCHAR(50))
INSERT INTO #T1(ID)
VALUES(1)
INSERT INTO #T2(ID, UDValue)
VALUES(1, 'Tom')
,(1, 'Dick')
,(1, 'Harry')
,(2, 'Chevy')
,(3, 'Apple')
,(2, 'Ford')
UPDATE #T1
SET UDValue = COALESCE(t1.UDValue, '') + t2.UDValue + ','
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t2.ID = t1.ID
SELECT * FROM #T1
DROP TABLE #T1
DROP TABLE #T2
So what I am looking for is to see my data like this:
ID UDValue
1, Tom,Dick,Harry
2, Chevy,Ford
3, Apple
but this is what I am getting:
ID UDValue
1 Tom,
2 Chevy,
3 Apple,
I want to avoid having to loop through each row but I don't see any alternatives.
you can use stuff with for xml path to concatenate column values
you can use a corelated sub query to get the comma separated values
Also it is not a good idea to store it as comma separated values in the database.
;with cte
as
(
select ID,
stuff((select ','+ T2.UDValue
from #T2 T2
where T2.ID = T1.ID
FOR XML PATH ('')), 1,1,'') as NewValue
from #T1 T1
)
update #T1
set UDValue = cte.NewValue
from cte
join #T1
on cte.ID = #T1.ID
select * from #T1
Concatenating Value in Update:
create table #T (Id int, Value varchar(50), primary key (Id, value));
declare #Id int;
declare #Value varchar(500);
insert into #T
( Id , Value )
values
( 1 , 'Tom' ),
( 1 , 'Dick' ),
( 1 , 'Harry' ),
( 2 , 'Chevy' ),
( 3 , 'Apple' ),
( 2 , 'Ford' );
update #T set
#Value = case when #Id is null or #Id = Id then #Value else null end,
#Value = Value = coalesce(#Value + ', ', '') + Value,
#Id = Id;
select Id, max(Value) from #T group by Id;
drop table #T;
The example works only if "primary key" is defined on the table.
More about "Quirky Update" is in Solving the Running Total and Ordinal Rank Problems
This is what I have come up with so far but I am not sure that it is the most efficient way to do this:
CREATE TABLE #T1(ID INT, UDValue NVARCHAR(50))
CREATE TABLE #T2(ID INT, UDValue NVARCHAR(50))
INSERT INTO #T1(ID)
VALUES(1)
,(2)
,(3)
INSERT INTO #T2(ID, UDValue)
VALUES(1, 'Tom')
,(1, 'Dick')
,(1, 'Harry')
,(2, 'Chevy')
,(3, 'Apple')
,(2, 'Ford')
DECLARE #id INT = 1, #UDValue NVARCHAR(MAX)
WHILE(#ID < 4)
BEGIN
SELECT #UDValue = STUFF((SELECT DISTINCT N',' + UDValue
FROM
#T2
WHERE ID = #ID
ORDER BY N',' + UDValue
FOR XML PATH(''), TYPE
).value(N'.[1]',N'nvarchar(max)'),1,1,'');
UPDATE #T1
SET UDValue = #UDValue
FROM
#T1 AS t1
WHERE
t1.ID = #ID
SELECT #ID += 1
END
SELECT * FROM #T1
DROP TABLE #T1
DROP TABLE #T2

SQL - Determine the most frequently occuring words within a column

Is there an easy way of determining the most frequently occuring word in a column/field using T-SQL or VBA?
I am working on a fuzzy matching system for two given recordsets and would like to produce a matching string where the most frequently occuring words are removed. As the data is from a customer relations management database terms like "limited", "ltd", "plc" and "CORPORATION" would be removed.
Written for sql-server 2005+
Function to split:
create function f_split
(
#a varchar(max),
#delimiter varchar(20)
)
RETURNS #t TABLE(substr varchar(200))
as
begin
set #a = #a + #delimiter
;with a as
(
select cast(1 as bigint) f1, charindex(#delimiter, #a) f2
where len(#a) > 0
union all
select f2 + (len(#delimiter)) + 1, charindex(#delimiter, #a, f2+1)
from a
where f2 > 0
)
insert #t
select substring(#a, f1, f2 - f1) from a
where f1 < f2
return
end
go
Query:
--testdata
declare #table table(name varchar(50))
insert #table values('bla bla bla ltd')
insert #table values('bla plc ltd')
insert #table values('more text CORPORATION')
declare #matchlist table(name varchar(50), replacement varchar(50))
insert #matchlist values('ltd', 'limited')
insert #matchlist values('plc', 'limited')
insert #matchlist values('CORPORATION', 'limited')
--query
select coalesce(m.replacement, a.substr) name, count(*) count from #table p
cross apply
(
select substr from
dbo.f_split(p.name, ' ')
) a
left join
#matchlist m
on a.substr = m.name
group by coalesce(m.replacement, a.substr)
order by 2 desc
Result:
name count
---- -----
bla 4
limited 4
more 1
text 1
Hope this will be useful to you.
create table sometable
( id integer not null primary key identity
, mYWords text not null
);
insert into sometable (mYWords)
values ('a word that appears maximum number of times in a column')
insert into sometable (mYWords)
values ('Is it possible to get words from text columns in a sql server database')
insert into sometable (mYWords)
values ('This could solve my problem if reffered column contain only single word')
insert into sometable (mYWords)
values ('that''s going to require that you split out every word in the column individually')
insert into sometable (mYWords)
values ('the query will definitely not be easy to write')
insert into sometable (mYWords)
values ('Please read the sticky at the top of the board')
insert into sometable (mYWords)
values ('The physical order of data in a database has no meaning')
GO
CREATE TABLE WordList (
Word varchar(256)
, WordId int IDENTITY(1,1)
, Add_Dt datetime DEFAULT (GetDate()))
GO
CREATE UNIQUE INDEX UnqueWords_PK ON WordList(Word)
GO
CREATE PROC isp_INS_WORD_LIST
AS
BEGIN
SET NOCOUNT ON
DECLARE #Words INT, #Pos INT, #x Int, #str varchar(256)
, #word varchar(256), #start int, #end int, #exitstart int
SELECT #Words = 0, #Pos = 1, #x = -1, #Word = '', #start = 1
DECLARE Cur1 CURSOR FOR SELECT mYWords FROM sometable
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #str
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE (#x <> 0)
BEGIN
SET #x = CHARINDEX(' ', #str, #Pos)
IF #x <> 0
BEGIN
SET #end = #x - #start
SET #word = SUBSTRING(#str,#start,#end)
IF NOT EXISTS (SELECT * FROM WordList WHERE Word = #Word)
INSERT INTO WordList(Word) SELECT #word
-- SELECT #Word, ##ROWCOUNT,##ERROR
-- SELECT #x, #Word, #start, #end, #str
SET #exitstart = #start + #end + 1
SET #Pos = #x + 1
SET #start = #x + 1
SET #Words = #Words + 1
END
IF #x = 0
BEGIN
SET #word = SUBSTRING(#str,#exitstart,LEN(#str)-#exitstart+1)
IF NOT EXISTS (SELECT * FROM WordList WHERE Word = #Word)
INSERT INTO WordList(Word) SELECT #word
-- SELECT #Word, ##ROWCOUNT,##ERROR
-- SELECT #x, #Word, #exitstart, LEN(#str)-#exitstart, #str
END
END
FETCH NEXT FROM Cur1 INTO #str
SELECT #Words = 0, #Pos = 1, #x = -1, #Word = '', #start = 1
END
CLOSE Cur1
DEALLOCATE Cur1
SET NOCOUNT OFF
RETURN #Words
END
GO
EXEC isp_INS_WORD_LIST
GO
SELECT * FROM WordList ORDER BY Word
GO
DROP PROC isp_INS_WORD_LIST
DROP TABLE WordList, sometable
GO

Help with store procedure

I need help with the following sp. Couldnt solve it really.
I have 2 columns in a table:
1. Parent
2. Child
I need to create a sp using rownum that will create a new table with a given param (level num) that will display the connection between the parent and son, or grandfather & grandson, etc...
For instance:
Parent | Child
2 4
4 6
the sp will return with a given level of 1 the same table, but if I hand it level 2 (grandfather<-> grandson) it will show:
Parent | Child
2 6
How can I do that?
Thanks all!
Here is some code using a recursive cte that does what I believe it is you want.
-- Sample data
declare #T table (ID int, ParentID int)
insert into #T values
(1, null),
(2, 1),
(3, 1),
(4, 3),
(5, 3),
(6, 1),
(7, null),
(8, 7),
(9, 8),
(10, 9)
-- The level you want
declare #Level int = 2
-- recursive cte
;with cte as
(
select
ID,
ParentID,
0 as lvl
from #T
where ParentID is null
union all
select
T.ID,
T.ParentID,
C.lvl+1 as lvl
from #T as T
inner join cte as C
on T.ParentID = C.ID
where C.lvl < #Level
)
select *
from cte
where lvl = #Level
DECLARE #level INT
SET #level = 3
DECLARE #temp TABLE
(
parent INT,
child INT
)
DECLARE #result TABLE
(
parent INT,
child INT
)
INSERT INTO #result
SELECT * FROM testing
WHILE #level > 1
BEGIN
SET #level = #level - 1
DELETE #temp
INSERT INTO #temp
SELECT t.parent, r.child
FROM testing t
INNER JOIN #result r
ON t.child = r.parent
DELETE #result
INSERT INTO #result
SELECT * FROM #temp
END
SELECT * FROM #result
You can use auto join. For level 2:
SELECT
p.Parent
,c.Child
FROM
Table_1 p
JOIN Table_1 c
ON p.Child = c.Parent
level 3:
SELECT
g.Parent
,c.Child
FROM
Table_1 g
JOIN Table_1 p
ON g.Child = p.Parent
JOIN Table_1 c
ON p.Child = c.Parent
If you need solution for arbitrary level, try to generate dinamic sql based on level.
CREATE PROCEDURE GetConnected
#level INT
AS
BEGIN
DECLARE #sql VARCHAR(1000)
SET #sql = 'SELECT t0.Parent, t' + CAST(#level AS VARCHAR(5)) + '.Child FROM Table_1 t0 '
DECLARE #counter INT
SET #counter = 0
WHILE #counter < #level
BEGIN
SET #sql = #sql + 'JOIN Table_1 t' + CAST(#counter + 1 AS VARCHAR(5)) + ' ON t' + CAST(#counter AS VARCHAR(5)) + '.Child = t' + CAST(#counter + 1 AS VARCHAR(5)) + '.Parent '
SET #counter = #counter + 1
END
EXEC (#sql)
END
This solution, for which I've shamelessly borrowed Mikael's sample data definition, shows all the relations represented, building pairs not only from the top level (parentless) items, but also from the lower level ones. The author of the question didn't specify whether it should be one way or the other.
/* Sample data */
DECLARE #T TABLE (ID int, ParentID int);
INSERT INTO #T VALUES
(1, NULL),
(2, 1),
(3, 1),
(4, 3),
(5, 3),
(6, 1),
(7, NULL),
(8, 7),
(9, 8),
(10, 9);
/* The level you want */
DECLARE #Level int;
SET #Level = 2;
/* The query */
WITH recur AS (
SELECT
ID,
AncestorID = ParentID,
Level = CASE WHEN ParentID IS NULL THEN 0 ELSE 1 END
FROM #T
UNION ALL
SELECT
r.ID,
t.AncestorID,
Level = r.Level + 1
FROM #T t
INNER JOIN recur r ON t.ID = r.AncestorID
WHERE r.Level < #Level
)
SELECT ID, ParentID
FROM recur
WHERE Level = #Level
AND (#Level = 0 OR ParentID IS NOT NULL)
For the given #Level value of 2 the result will be:
ID AncestorID Level
----------- ----------- -----------
10 8 2
9 7 2
5 1 2
4 1 2

Resources