Update field in table - sql-server

I have the following table PNLReference
PnlId LineTotalisationId Designation TypeTotalisation Totalisation
1 A Gross Fees Formule A01+A02+A03+A04+A05
2 A01 GF1 Comptes imputables 975800|758000|706900|706000|706430|706420|706410|706400|706530|706520|706510|706001|706401|706431|706531|706902
3 A02 GF2 Comptes imputables 706500|709400|706130|706120|706110|706100|706830|706820|706810|706800|706730|706720|706710|706700|706330|706101|706131|706331|706501|706701|706801|706831|709401|706731
I have filled table DimPNL as following
INSERT [dbo].[DimPNL] (
PNLCode
,PNLName
,PNLParentId
,Operator
)
SELECT *
FROM (
SELECT t.c.value('.', 'nvarchar(255)') AS PNLCode
,Ref.Designation AS PNLName
,split.LineTotalisationId AS PNLParentId
,split.Operator AS Operator
FROM (
SELECT tbl.Designation
,tbl.LineTotalisationId
,tbl.TypeTotalisation
,tbl.PnlId
,tbl.Totalisation
,CAST('<t>' + REPLACE(tbl.Totalisation, tbl.Operator, '</t><t>') + '</t>' AS XML) x
,tbl.Operator
FROM ##TTResults AS tbl
) split
CROSS APPLY x.nodes('/t') t(c)
INNER JOIN [dbo].[PNLReference] Ref
ON Ref.LineTotalisationId = t.c.value('.', 'nvarchar(255)')
) Result
table dimpnl contents a filed sign which have to be filled like that : if all numbers in Totalisation in table PNLReference starts with 7 the sign would be -1 else sign will be 1.How to do it ? any idea ?

Using CASE WHEN LEFT(Totalisation,1)='7' then -1 else 1 END [SIGN] will give you a field that you can take the MAX(sign), if it stays -1 then they all started with 7

Related

TSQL Compare tables based on multiple rows same column?

i will try and be as clear as possible on this one, as i have no idea what to do next and would love a kick in the right direction.
Im trying to compare the values within 2 tables. The tables look like this:
Table1:
Table2
INSERT INTO #table1 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
INSERT INTO #table2 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
I used this code to fill the testtables. Normally this is an automated process, and the tables are filled from an XML string.
Furthermore, the numbers in the element name are considered "groups" meaning T1 P1 and N1 are together.
I would like to compare T1 and P1 etc from Table1 to any combination of T and P from table2
If they match, i would like to overwrite the value of Table 1 N1 with the value of the matched N on table 2. (in the example, table1 N3 would be replaced with table2 N2
Besides that i also want to keep every group in table 1 that is not in table 2
but also add every group that is in table 2 but not in table 1 on one of the blank spots.
Last but not least, if the T value is filled, but P value is empty, it does not have to overwrite/change anything in table1.
The expected result would be this:
Table1:
i made the changes bold.
I dont really have an idea on where to start on this. Ive tried functions as except and intersect, but did not get even close to what i would like to see.
with t1 as (
select * from (values
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
) v([elementName], [elementValue])
),
t2 as (
select * from (values
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
) v([elementName], [elementValue])
),
pivoted_t1 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t1) t1
pivot(min(value) for letter in ([t], [p], [n])) pvt1
),
pivoted_t2 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t2) t2
pivot(min(value) for letter in ([t], [p], [n])) pvt2
),
amended_values as (
select
pvt1.number,
coalesce(pvt2.t, pvt1.t) as t,
coalesce(pvt2.p, pvt1.p) as p,
coalesce(pvt2.n, pvt1.n) as n,
count(case when pvt1.t = '' and pvt1.p = '' then 1 end) over(order by pvt1.number rows between unbounded preceding and current row) as empty_row_number
from
pivoted_t1 pvt1
left join pivoted_t2 pvt2 on pvt1.t = pvt2.t and pvt1.p = pvt2.p and pvt1.t <> '' and pvt1.p <> ''
),
added_new_values as (
select
a.number,
coalesce(n.t, a.t) as t,
coalesce(n.p, a.p) as p,
coalesce(n.n, a.n) as n
from
amended_values a
left join (
select number, t, p, n, row_number() over (order by number) as row_number
from pivoted_t2 t2
where
t2.t <> ''
and t2.p <> ''
and not exists (select * from pivoted_t1 t1 where t1.t = t2.t and t1.p = t2.p)
) n on n.row_number = a.empty_row_number
)
select
concat([elementName], number) as [elementName],
[elementValue]
from
added_new_values
unpivot ([elementValue] for [elementName] in ([t], [p], [n])) upvt
;

Issue in complicated join

I have 4 tables
tbLicenceTypesX (2 Fields)
LicenceTypes
LicenceTypesX
tbLicenceTypesX (Contains data like)
1 - Medical Licence
2 - Property
3 - Casualty
4 - Trainning Licence
tbProduct (3 feilds)
Product
ProductX
CompanyId (F.K)
LicenceTypes(F.K)
tbProduct (Contains data like)
1 - T.V - 10 - 2
2 - A.C - 30 - 3
3 - Mobiles - 40 -4
tbLicence (3 feilds)
Licence
LicenceTypesNames
AgentId
tbLicence (Contains data like)
1 - Property, Casualty - 23
2 - Trainning Licence, Casualty - 34
Now I have to Fetch Product and ProductX from tbProduct whose LicenceTypes matches with Agent's Licence in tbLicence in a Company.
For e.g: I have to fetch T.V Whose Licence Types is 2("Property") and Company Id is 10 which should be assigned to Agent where Agent Id is 23 and Whose LicenceTypesNames should also contains "Property"
I want to fetch something like
#CompanyId int,
#AgentId int
As
SELECT p.ProductX,p.Product
from tbProduct p
inner join tbLicence l on p.LicenceTypes = l.LicenceTypesNames<its corresponding Id>
inner join tbProduct c on c.Product =p.Product
where
c.CompanyId=#CompanyId
and l.AgentId=#AgentId
Please help me!!!
You can use XML and CROSS APPLY to Split the comma separated values and JOIN with tbProduct. The LTRIM and RTRIM functions are used to trim the comma separated values if they have excessive empty space. The below code gives you the desired output.
DECLARE #CompanyId int = 30, #AgentId int = 23
;WITH CTE AS
(
SELECT AgentId, TCT.LicenceTypes FROM
(
SELECT AgentId, LTRIM(RTRIM(Split.XMLData.value('.', 'VARCHAR(100)'))) LicenceTypesNames FROM
(
SELECT AgentID, Cast ('<M>' + REPLACE(LicenceTypesNames, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM tbLicence
) AS XMLData
CROSS APPLY Data.nodes ('/M') AS Split(XMLData)
)
AS LTN
JOIN tbLicenceTypesX TCT ON LTN.LicenceTypesNames = tct.LicenceTypesX
)
SELECT p.ProductX,p.Product
FROM tbProduct P
JOIN CTE c on p.LicenceTypes = c.LicenceTypes
WHERE CompanyId = #CompanyId
AND AgentId = #AgentId
Sql Fiddle Demo

SQL server: WHERE in xml field

Example: I have table TableA with 3 records:
record 1:
id = 1, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee>'
record 2:
id = 2, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee>'
record 3:
id = 3, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee><Employee id='4' name='Employee4'></Employee>'
the query:
SELECT * FROM TableA
WHERE...
How can I put the where clause to get only record 1?
Many thanks,
The problem with the data is that it doesn't contain well formed xml - you will need to wrap it before you can use the xml tools in Sql like xquery.
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName
FROM
(
SELECT CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE
y.EmployeeId = 1;
Inner select -wraps the xml
Middle select - standard xquery
Outer select - where filter
You haven't clarified what you mean w.r.t. get only record 1, but if you mean just the first element of each row (which coincidentally also has id = 1), you can use ROW_NUMBER() to assign a sequence:
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName,
ROW_NUMBER() OVER (PARTITION BY x.Id ORDER BY ( SELECT 1 )) SequenceId
FROM
(
SELECT Id, CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE SequenceId = 1;
Both Fiddles here
I tried with this query and It returns record 1 as I expect:
SELECT * FROM TableA
WHERE value.exist('(Employee[#id = 1])') = 1 and value.exist('(Employee[#id = 2])') = 1 AND value.value('count(Employee[#id])', 'int') = 2
Do you have any comments for this query? Should I use it? :)

how to write nested query

I have a table named Options. have Three fields Caption, OptionID, ParentOptionID.
it contains some records like :
OptiondID Caption ParentOptionID
1 Entry 0
2 Sale 1
3 Sale Invoice 2
----------------------------------------------
I want the result as :
OptiondID Caption ParentOptionID
1 Entry 0
2 Entry - Sale 1
3 Entry - Sale - Sale Invoice 2
-----------------------------------------------
Option Caption of its parent option - added in current Options Caption, and it should be nested.
This is the query that I have tried:
;with MyRelation as (
-- Anchor member definition
select OID, Cast(Caption as Varchar(1000)) as Caption, POID, iid
from #tmpOptions as e
UNION ALL
-- Recursive member definition
select e.OID, Cast(e.Caption + '-' + r.Caption as Varchar(1000)) as Caption, e.POID, e.iid
from #tmpOptions as e join MyRelation R on e.POID = R.OID
)
-- Statement that executes the CTE
select OID, Caption, POID, iid
from MyRelation
Could you tried with below query
;WITH MyRelation AS (
SELECT OptiondID, convert(varchar(max), Caption) AS Caption, ParentOptionID
FROM Options
WHERE ParentOptionID = 0
UNION ALL
SELECT Options.OptiondID, MyRelation.Caption + ' - ' + Options.Caption, Options.ParentOptionID
FROM Options
INNER JOIN MyRelation ON Options.ParentOptionID = MyRelation.OptiondID
WHERE Options.ParentOptionID <> 0
)
SELECT * FROM MyRelation

SQL Server 2008 - Conditional Range

I have a database that has two tables. These two tables are defined as:
Movie
-----
ID (int)
Title (nvchar)
MovieReview
-----------
ID (int)
MovieID (int)
StoryRating (decimal)
HumorRating (decimal)
ActingRating (decimal)
I have a stored procedure that allows the user to query movies based on other user's reviews. Currently, I have a temporary table that is populated with the following query:
SELECT
m.*,
(SELECT COUNT(ID) FROM MovieReivew r WHERE r.MovieID=m.ID) as 'TotalReviews',
(SELECT AVG((r.StoryRating + r.HumorRating + r.ActingRating) / 3)
FROM MovieReview r WHERE r.MovieID=m.ID) as 'AverageRating'
FROM
Movie m
In a later query in my procedure, I basically want to say:
SELECT
*
FROM
MyTempTable t
WHERE
t.AverageRating >= #lowestRating AND
t.AverageRating <= #highestRating
My problem is, sometimes AverageRating is zero. Because of this, I'm not sure what to do. How do I handle this scenario in SQL?
First, I'd used a derived table to calculate the statistics. This makes it easier to convert nulls to zero if you want (although if we're using an Inner Join as I am here, you couldn't need to use Coalesce on the TotalReviews).
Select m.*
, Coalesce(MovieStats.TotalReviews, 0) As TotalReviews
, Coalesce(MovieStats.AverageRating, 0) As AverageRating
From Movie As m
Join (
Select R1.MovieId
, Count(*) As TotalReviews
, AVG( (r.StoryRating + r.HumorRating + r.ActingRating) / 3 ) As AverageRating
From MovieReview As r1
Group By R1.MovieId
) As MovieStats
On MovieStats.MovieId = m.Id
Where MovieStats.AverageRating Between #LowestRating And #HighestRating
The only issue here is that by putting our filter in the Where clause, it means we must have MovieReview records with AverageRating values in given range.
Select m.*
, Coalesce(MovieStats.TotalReviews, 0) As TotalReviews
, Coalesce(MovieStats.AverageRating, 0) As AverageRating
From Movie As m
Left Join (
Select R1.MovieId
, Count(*) As TotalReviews
, AVG( (r.StoryRating + r.HumorRating + r.ActingRating) / 3 ) As AverageRating
From MovieReview As r1
Group By R1.MovieId
) As MovieStats
On MovieStats.MovieId = m.Id
And MovieStats.AverageRating Between #LowestRating And #HighestRating
This will return zero in those instances where there are no movie ratings or the movie ratings that do exist are outside the passed range.
Yet another possibility is to filter on values that have Reviews and force those to have average ratings in the passed range:
Select m.*
, Coalesce(MovieStats.TotalReviews, 0) As TotalReviews
, Coalesce(MovieStats.AverageRating, 0) As AverageRating
From Movie As m
Left Join (
Select R1.MovieId
, Count(*) As TotalReviews
, AVG( (r.StoryRating + r.HumorRating + r.ActingRating) / 3 ) As AverageRating
From MovieReview As r1
Group By R1.MovieId
) As MovieStats
On MovieStats.MovieId = m.Id
Where MovieStats.MovieId Is Null
Or ( MovieStats.AverageRating Between #LowestRating And #HighestRating )
I'm assuming you want to movies with zero reviews included in your result.
SELECT
*
FROM
MyTempTable t
WHERE
(t.AverageRating >= #lowestRating AND
t.AverageRating <= #highestRating)
OR t.TotalReviews = 0
It's safe to test TotalReviews, because it will never be NULL.

Resources