SQL: how to use loop in PIVOT - sql

My SQL is:
SELECT * FROM
(SELECT
cmdoc.documentno 'DRG. NO.',
cmdoc.subject 'TITLE',
CAST(documentno AS VARCHAR(255)) AS docno,
CAST(cmrev.code AS VARCHAR(255)) AS code
FROM cmdocument cmdoc
INNER JOIN cmdocumentrevision cmrev
ON cmdoc.cmdocument = cmrev.cmdocument
INNER JOIN cmdocumentdefn
ON cmdoc.cmdocumentdefn = cmdocumentdefn.cmdocumentdefn
WHERE
cmdocumentdefn.isrevisable = 1
) x
PIVOT(COUNT(docno) FOR code IN([A],[B],[C],[D],[E],[F],[G])) pvt
I want to loop the line inside IN('/* here */').
If possible, I want the start and end to be an SQL like:
-- start
SELECT MIN(code) FROM table
-- end
SELECT MAX(code) FROM table
how can I do it?

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 - Update more rows than I expected

I'm trying to update a part of my table. If I do a select statement, I find 17 ocurrences, but when I update it, it updates 997 ocurrences. I only want to update the 17 ocurrences. This is my code:
update proc_try k set detail = (
select jobs from
(
with
a ( nameHost ) as (
select b.nameHost
from definition a ,schema.nodes b
where b.nameHost = a.idNode or b.nodeid=a.idNode
and nodetype not like 'R'
group by b.nameHost
having sum(1 + lengthb(nameJob)) - 1 > 4000
)
select nameHost, 'TOOLONG' as jobs
from a
UNION ALL
select p.nameHost, listagg(p.nameJob,',') within group (order by p.nameJob) as jobs
from
(
select distinct b.nameJob, a.nameHost
from definition b
right join schema.nodes a
on b.idNode in (a.nodeid,a.nameHost) and
b.application not like '#NOTINCLUDE'
where a.nameHost not in (select * from a) and nodetype not like 'R'
--b.application not like '#NOTINCLUDE'
) p
group by p.nameHost) random
where k.nameHost=random.nameHost);
Could you help me please?
You can generally convert a complex update into a merge:
merge into proc_try k
using
( select jobs
from ( with a(namehost) as
( select b.namehost
from definition a
join schema.nodes b
on b.namehost = a.idnode
or (b.nodeid = a.idnode and nodetype <> 'R')
group by b.namehost
having sum(1 + lengthb(namejob)) - 1 > 4000 )
select namehost
, 'TOOLONG' as jobs
from a
union all
select p.namehost
, listagg(p.namejob, ',') within group(order by p.namejob) as jobs
from ( select distinct
b.namejob, a.namehost
from schema.nodes a
left join definition b
on b.idnode in (a.nodeid, a.namehost)
and b.application not like '#NOTINCLUDE'
where a.namehost not in (select * from a)
and nodetype not like 'R'
) p
group by p.namehost
) random
) new_jobs
on (k.namehost = new_jobs.namehost)
when matched then update set k.detail = new_jobs.jobs;
This is untested as I don't have your tables or sample data.
Edit: Looks like we can simplify it a bit, to this:
merge into proc_try k
using
( with overlength (namehost) as
( select n.namehost
from definition d
join schema.nodes n
on n.namehost = d.idnode
or (n.nodeid = d.idnode and nodetype <> 'R')
group by n.namehost
having sum(1 + lengthb(n.namejob)) - 1 > 4000 )
select o.namehost, 'TOOLONG' as jobs
from overlength o
union all
select sd.namehost
, listagg(sd.namejob, ',') within group(order by sd.namejob) as jobs
from ( select distinct d.namejob, n.namehost
from schema.nodes n
left join definition d
on d.idnode in (n.nodeid, n.namehost)
and d.application not like '#NOTINCLUDE'
where n.namehost not in (select o.namehost from overlength o)
and n.nodetype not like 'R'
) sd
group by sd.namehost
) new_jobs
on (new_jobs.namehost = k.namehost)
when matched then update set k.detail = new_jobs.jobs;
I still can't see what
sum(1 + lengthb(namejob)) - 1
is meant to do, though. It looks like that could be simplified to
sum(lengthb(namejob))

Create view with with statement

How to create view with a with statement?
I'm getting on error on it:
WITH temp as (
select uu.email, u.logintime, u.region, p.id as panelid, p.panelname, p.numberofdownloads, dimensionType + ' (' + dimensionValue + ')' as filter
from stat_users u
left join stat_panels p
on u.id=p.sessionid
left join stat_filters f
on p.id=f.panelid
left join users uu
on uu.id=u.userid
where uu.Organization = 'name' AND
year(logintime) between 2015 and 2017
and panelname is not null
)
CREATE VIEW final as(
select aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads as downloads, case when len(aa.filters) > 0 then left(aa.filters, len(aa.filters)-1) else '' end as filters
from (
Select distinct a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
Select b.filter + ', ' AS [text()]
From temp b
Where b.panelid=a.panelid
ORDER BY b.panelid
For XML PATH ('')
) filters
from temp a
) aa
)
I'm getting such error :
> Incorrect syntax near the keyword 'CREATE'. 'CREATE VIEW' must be the
> first statement in a query batch.
So, I need just to use Create view using select which based on WITH statement on Sql server 2014
Yes always the CREATE has to be the first statement in a query batch
CREATE VIEW vFinal AS
WITH Temp AS (
SELECT uu.email, u.logintime, u.region, p.id AS panelid, p.panelname, p.numberofdownloads, dimensionType + ' (' + dimensionValue + ')' AS Filter
FROM stat_users u
LEFT JOIN stat_panels p ON u.id=p.sessionid
LEFT JOIN stat_filters f ON p.id=f.panelid
LEFT JOIN users uu ON uu.id=u.userid
WHERE uu.Organization = 'name' AND
YEAR(logintime) BETWEEN 2015 AND 2017
AND panelname IS NOT NULL
)
SELECT aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads AS downloads, CASE WHEN LEN(aa.filters) > 0 THEN LEFT(aa.filters, LEN(aa.filters)-1) else '' end as filters
FROM (
SELECT DISTINCT a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
SELECT b.filter + ', ' AS [text()]
FROM temp b
WHERE b.panelid=a.panelid
ORDER BY b.panelid
FOR XML PATH ('')
) filters
FROM temp a
) aa
GO
Syntax to create a view table using CTE
CREATE VIEW View_Name AS
WITH CTE_Name (Columns) AS (SELECT QUERY)
SELECT QUERY using the CTE Table
GO
The with clause is an optional prefix for select:
WITH query_name (column_name1, ...) AS
(SELECT ...)
SELECT ...
This is also true when with is used in a view:
CREATE VIEW ...
WITH ...
SELECT ...
;
See also: http://modern-sql.com/feature/with
CREATE or replace VIEW final as
select aa.email, aa.logintime, aa.region, aa.panelname, aa.numberofdownloads as downloads, case when len(aa.filters) > 0 then left(aa.filters, len(aa.filters)-1) else '' end as filters
from (
Select distinct a.email, a.logintime, a.region, a.panelname, a.numberofdownloads,
(
Select b.filter + ', ' AS [text()]
From temp b
Where b.panelid=a.panelid
ORDER BY b.panelid
For XML PATH ('')
) filters
from temp a )

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.

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary