How to check intersection of subqueries in query? - sql

I have the next query:
SELECT c.client_code, a.account_num, m.account_close_date, u.uso, m.product_name
FROM accounts a INNER JOIN Clients c ON c.id = a.client_id INNER JOIN
Uso u ON c.uso_id = u.uso_id INNER JOIN Magazine m ON a.account_id = m.account_id
and I need to compare product_name with input parameter.
product_name and input parameter #s are comma-delimited strings.
I use next split function:
ALTER FUNCTION [dbo].[Split]
(
#s VARCHAR(max),
#split CHAR(1)
)
RETURNS #temptable TABLE (items VARCHAR(MAX))
AS
BEGIN
DECLARE #x XML
SELECT #x = CONVERT(xml,'<root><s>' + REPLACE(#s,#split,'</s><s>') + '</s></root>');
INSERT INTO #temptable
SELECT [Value] = T.c.value('.','varchar(20)')
FROM #X.nodes('/root/s') T(c);
RETURN
END;
I think that I need to check the intersection of tables, which I will receive after split of product_name and after split of input parameter. I trid to do this:
WHERE (select * from dbo.Split(m.product_name, ';')
INTERSECT select * from dbo.Split('product1;product2',';'))
is not null
But it does not work quite right. Please, help me.

INTERSECT requires the same column output and is used like UNION or EXCEPT: not in the WHERE clause
Just JOIN onto the udf
...
INNER JOIN
Magazine m ON a.account_id = m.account_id
INNER JOIN
dbo.Split(#parameter, ';') CSV ON m.productname = CSV.items
If you need to split m.productname, if you can't fix the design, use CROSS APPLY
...
INNER JOIN
Magazine m ON a.account_id = m.account_id
CROSS APPLY
dbo.Split(m.productname, ';') WTF
INNER JOIN
dbo.Split(#parameter, ';') CSV ON WTF.items = CSV.items
However, JOIN and INTERSECT give different results if #parameter has duplicated values. Add a DISTINCT to the UDF for example to get around this. Or change the udf JOIN into EXISTS

Related

Use Data of 1 table into another one dynamically

I have one table category_code having data like
SELECT Item, Code, Prefix from category_codes
Item Code Prefix
Bangles BL BL
Chains CH CH
Ear rings ER ER
Sets Set ST
Rings RING RG
Yellow GOld YG YG........
I have another table item_categories having data like
select code,name from item_categories
code name
AQ.TM.PN AQ.TM.PN
BL.YG.CH.ME.PN BL.YG.CH.ME.PN
BS.CZ.ST.YG.PN BS.CZ.ST.YG.PN
CR.YG CR.YG.......
i want to update item_categories.name column corresponding to category_code.item column like
code name
BL.YG.CH.ME.PN Bangles.Yellow Gold.Chains.. . . .
Please suggest good solution for that. Thanks in advance.
First, split the code into several rows, join with the category code and then, concat the result to update the table.
Here an example, based on the data you gave
create table #category_code (item varchar(max), code varchar(max), prefix varchar(max));
create table #item_categories (code varchar(max), name varchar(max));
insert into #category_code (item, code, prefix) values ('Bangles','BL','BL'),('Chains','CH','CH'),('Ear rings','ER','ER'), ('Sets','Set','ST'),('Rings','RING','RG'), ('Yellow gold','YG','YG');
insert into #item_categories (code, name) values ('AQ.TM,PN','AQ.TM.PN'),('BL.YG.CH.ME.PN','BL.YG.CH.ME.PN'),('BS.CZ.ST.YG.PN','BS.CZ.ST.YG.PN')
;with splitted as ( -- split the codes into individual code
select row_number() over (partition by ic.code order by ic.code) as id, ic.code, x.value, cc.item
from #item_categories ic
outer apply string_split(ic.code, '.') x -- SQL Server 2016+, otherwise, use another method to split the data
left join #category_code cc on cc.code = x.value -- some values are missing in you example, but can use an inner join
)
, joined as ( -- then joined them to concat the name
select id, convert(varchar(max),code) as code, convert(varchar(max),coalesce(item + ',','')) as Item
from splitted
where id = 1
union all
select s.id, convert(varchar(max), s.code), convert(varchar(max), j.item + coalesce(s.item + ',',''))
from splitted s
inner join joined j on j.id = s.id - 1 and j.code = s.code
)
update #item_categories
set name = substring (j.item ,1,case when len(j.item) > 1 then len(j.item)-1 else 0 end)
output deleted.name, inserted.name
from #item_categories i
inner join joined j on j.code = i.code
inner join (select code, max(id)maxid from joined group by code) mj on mj.code = j.code and mj.maxid = j.id

SQL Server : passing company id (int) with comma in where condition [duplicate]

This question already has answers here:
How to split a comma-separated value to columns
(38 answers)
Closed 4 years ago.
I am working in SQL Server 2008. I am facing a problem in a where condition, passing multiple company ID values separated by comma (,) to use in Int company ID.
ALTER Procedure
[dbo].[SP_WorkOrderDetails] #CompanyID varchar(50)
as begin
Select
*
from
ClientContract C
left join
WorkOrder W
on
C.Id=W.ClientContractId
left join
ClientContractContacts CB
on
CB.ClientContractld=C.Id
left join
Client CL
on
CL.Id= C.Clientld
left join
Client CN
on
CN.Id= CB.Contactld
left join
BaseUnit B
on
B.Id=W.BaseUnitId
left join
users u
on
u.Staffld = W.AssignedTo
left join
LatticeERP_DFS..sv_App_Staff SPT
on
SPT.Leaderld=W.AssignedTo and
SPT.Module = 'S'
left join
Staff SS
on
SS.Id=u.StaffId --and ss.IsTeamLeader = 1
left join
Companies Comp
on
Comp.id=C.Companyld
Where
convert(nvarchar(50),isnull(C.companyID, 1)) in (replace('1,7', '''', ','))
Order By
ContractCode,
WorkOrderNo Desc
end
can you try this
IN (select cast ( STRING_SPLIT ( '1,7' , ',' ) as int ) )
or may be this
IN ( SELECT
Split.a.value('.', 'VARCHAR(100)') AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE('1,7', ',', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a) )
you can use this table-valued function:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
create FUNCTION dbo.SplitString(#str VARCHAR(4000),#seprator VARCHAR(1))
RETURNS TABLE
AS
RETURN ( WITH tokens(p,a,b) AS (SELECT 1,1,CHARINDEX(#seprator,#str)
UNION ALL SELECT p+1,b+1,CHARINDEX(#seprator,#str,b+1)
FROM tokens WHERE b>0
)
SELECT
SUBSTRING(#str,a,CASE WHEN b > 0 THEN b-a ELSE 4000 end)
AS VALUE
FROM tokens)
GO
use it like:
... where ID In(select value from dbo.SplitString('1,7,...',','))

Existing query optimization

We have 5 tables and we are trying to create a view to get the results.
Below is the view which is working fine.
I need suggestions. Is it a good practice to write this query in this way or it can be optimized in a better way.
SELECT p.Pid, hc.hcid, hc.Accomodation, ghc.ghcid, ghc.ProductFeatures, wp.existing, wp.acute, mc.cardiaccover, mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc
ON p.pid = hc.pid
LEFT JOIN TableC ghc
ON p.pid = ghc.pid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title,
wperiodvalue + '-' + CASE WHEN
wperiodvalue > 1 THEN
unit +
's' ELSE
unit END wperiod
FROM TableD) d
PIVOT ( Max(wperiod)
FOR title IN (acute,
existing
) ) piv1) wp
ON hc.hcid = wp.hcid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title + col new_col,
value
FROM TableE
CROSS apply ( VALUES (cover,
'Cover'),
(Cast(limitationperiod AS
VARCHAR
(10)),
'LimitationPeriod') ) x (value, col
)) d
PIVOT ( Max(value)
FOR new_col IN (cardiaccover,
cardiaclimitationperiod,
cataracteyelenscover,
cataracteyelenslimitationperiod
) ) piv2) mc
ON hc.hcid = mc.hcid
Any suggestions would be appreciated.
Thanks
My suggestion is to break down the query using temporary table, create stored procedure then dump data in the one new table and with the help of that table you can create view:
Store both PIVOT result in tow seperate temp tables as
SELECT * INTO #pvtInfo FROM ( --first PIVOT query
SELECT * INTO #pvtInfoTwo FROM ( --second PIVOT query
Then your final query will be as :
SELECT p.Pid,
hc.hcid,
hc.Accomodation,
ghc.ghcid,
ghc.ProductFeatures,
wp.existing,
wp.acute,
mc.cardiaccover,
mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc ON p.pid = hc.pid
LEFT JOIN TableC ghc ON p.pid = ghc.pid
LEFT JOIN #pvtInfo wp ON hc.hcid = wp.hcid
LEFT JOIN #pvtInfoTwo mc ON hc.hcid = mc.hcid
First you can try then only go with SP and VIEW.
Hope, It will help.

How to Retrieve Column Headers of a Select Query?

How it is possible to retrieve column headers of a select query as a single column in SQL Server ? (it is preferred to retrieve data type of columns )
Query example:
select a.PartId, a.PartName, b.GroupName
from Parts as a
inner join Groups as b on a.GroupRef = b.GroupId
Expected result:
Columns
--------
PartId
PartName
GroupName
Starting from SQL Server 2012+ you can use sys.dm_exec_describe_first_result_set to get all metadata about result set:
DBFiddle Demo
DECLARE #tsql NVARCHAR(MAX) =
N'select a.PartId , a.PartName , b.GroupName
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId';
SELECT name AS [Columns]
FROM sys.dm_exec_describe_first_result_set(#tsql, NULL, 1)
One way is to create a temporary table with the schema of resultset and then query tempdb's schema table to get the column names and details. You can get all needed details.
select a.PartId , a.PartName , b.GroupName into #yourtable
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId
where 1=2
SELECT c.name as columnname,t.name as datatype
FROM tempdb.sys.columns c
inner join tempdb.sys.systypes as t on t.xtype = c.system_type_id
WHERE [object_id] = OBJECT_ID(N'tempdb..#yourtable');
SELECT 'PartId', 'PartName', 'GroupName'
UNION ALL
select a.PartId , a.PartName , b.GroupName
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId

Cross join on condition

I have a Stored Proc query below which involves returning partial delimited search string. E.g.searching passing a search string of 'wis,k' will return all results with ID that has 'wis' and 'k' in them. I am using a function and a cross join for this but the problem if attaching the cross join will prevent loading all my data which I will need to when I load this SPROC. I was thinking if a conditioned Cross Join is possible such that when my search string variable '#ReceiptNo' is null then I will omit the Cross Join and allow all my data to be displayed. Please kindly advice. Thanks.
Portion of my SPROC:
FROM [Transact] T
LEFT JOIN [Outlet] O On (T.Outlet_Code = O.Code)
LEFT JOIN [SystemCode] SC on (CONVERT(NVARCHAR,T.Mode) = SC.Code)
CROSS JOIN DBO.SPLIT(#ReceiptNo , ',') --SPLIT function to seperate delimited string
Where
(
CardNo In
(
Select [CardNo]
FROM [Card]
WHERE [CardNo] = #CardNo
AND [DeletedBy] IS NULL
AND [DeletedOn] IS NULL
AND [MemberID] = #MemberId
)
)
and
(
(T.TransactDate Between #TransactDateFrom And #TransactDateTo
or #TransactDateFrom is null
or #TransactDateTo is null
)
and (T.TransactDate >= #TransactDateFrom
or #TransactDateFrom is null)
and (T.TransactDate <= #TransactDateTo
or #TransactDateTo is null)
and
(
(',' + #Mode +',' LIKE '%,' + CONVERT(VARCHAR, T.Mode) + ',%')
or #Mode is null
)
and (T.ReceiptNo LIKE '%' + VAL + '%') --This is the 'LIKE' condition to return desired search string results
or (#TransactDateFrom is null
and #TransactDateTo is null
and #Mode is null
and #Outlet_Code is null
and #ReceiptNo is null
)
)
Group by T.AutoID, TransactDate,TransactTime, SC.Name, O.Name
, ReceiptNo, AmountSpent, TransactPoints, VoidOn
You need to take care of NULL and set it to any constant value. Modify CROSS JOIN to (read notes below query):
CROSS JOIN (SELECT ISNULL(Portion, 1) AS Portion FROM DBO.SPLIT(#ReceiptNo , ',')) TTT
In query above, Portion is column returned by DBO.SPLIT function. Change its name to appropriate and add more columns (with ISNULL) if needed.
Am I missing something or You can simply use LEFT JOIN instead of CROSS JOIN? Also, You might consider putting DBO.SPLIT function result into temporary table, index it and then use it in your CROSS/LEFT JOIN.
EDIT#1: I can't find any reason why You should not change CROSS JOIN to LEFT JOIN, as it will process less rows when #RecepitNo is not NULL.