So I have two tables: Agent and Conta.
Agents can have a lot of Conta but one Conta can only have one Agent.
I want to display just the Agents, to create a html table where you can see all of the agents present in the database - on a backoffice!
I'm using the following code, but the result I'm getting is one agent entry for each conta where he participated.
WITH LAST_COUNT AS
(SELECT {Conta}.[AgentId], {Conta}.[Data], {Conta}.[Valor],{Conta}.[Submit]
FROM {Conta})
SELECT
{Agent}.[Id], {Agent}.[Nome], {Agent}.[Apelido], LAST_COUNT.[Data],
LAST_COUNT.[Valor], LAST_COUNT.[Submit], {AgentPicture}.[Id],
{AgentPicture}.[Filename], {Agent}.[Telemovel], {Agent}.[UserId]
FROM {Agent}
LEFT JOIN {AgentPicture} ON {AgentPicture}.[Id] = {Agent}.[Id]
INNER JOIN LAST_COUNT ON LAST_COUNT.[AgentId] = {Agent}.[Id]
WHERE {Agent}.[Id] LIKE '%' + #SearchFilter + '%'
OR {Agent}.[Nome] LIKE '%' + #SearchFilter + '%'
OR {Agent}.[Apelido] LIKE '%' + #SearchFilter + '%'
OR #SearchFilter = ''
Can you help me? thanks!
In your query you are joining Agent table with LAST_COUNT common table expression (CTE). As a result you have every record from Contra in your results.
Just remove
WITH LAST_COUNT AS
(SELECT {Conta}.[AgentId], {Conta}.[Data], {Conta}.[Valor],{Conta}.[Submit]
FROM {Conta})
from your query and drop the last JOIN statement: LEFT JOIN LAST_COUNT ON LAST_COUNT.[AgentId] = {Agent}.[Id]
Also, do not forget to remove the following columns, which are retrieved from LAST_COUNT table:
LAST_COUNT.[Data],
LAST_COUNT.[Valor], LAST_COUNT.[Submit],
Related
The below query worked until I added a WHERE clause.
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
WHERE View_PersonToDirector.DirectorId = 12345 --No error if this line is removed
The error message is:
Invalid length parameter passed to the RIGHT function.
This leads me to believe that there was an error with how I wrote the View_PersonToDirector view. Something about adding the WHERE clause causes the query to be evaluated/optimized in a different way, exposing some issue.
Internals of View_PersonToDirector:
WITH items AS (
SELECT
PersonId,
0 AS [Level],
CAST(PersonId AS VARCHAR(255)) AS [Path]
FROM PersonTable
UNION ALL
SELECT
e.PersonId,
[Level] + 1,
CAST([Path] + ' < ' + CAST(e.PersonId AS VARCHAR(255)) AS VARCHAR(255))
FROM PersonTable e
INNER JOIN items itms ON itms.PersonId = e.ManagerId
)
SELECT
A.PersonId,
CASE
WHEN A.[Level] = 1
THEN A.PersonId
ELSE CAST(LEFT(A.PathToDirector, CHARINDEX(' ', A.PathToDirector)) AS INT)
END AS DirectorId
FROM (
SELECT
items.PersonId,
items.[Level],
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
FROM items
WHERE Path LIKE '1111 < %' --Id of director
) A
I suspect that having a WITH cte in the view causes the query optimizer to work differently, applying the WHERE filtering in a different order. Is this bad practice?
One of your problems is definitely in this code:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
I appreciate that you think that this WHERE clause fixes the problem:
WHERE Path LIKE '1111 < %'
But it does not. The problem is that SQL Server does not guarantee the order of evaluation of expressions. This true even for subqueries, CTEs, and views. These can be evaluated in any order. In fact, SQL Server sometimes pushes the evaluation to the node that reads from the table -- which is why you get an error.
Personally, I think this is a bug in SQL Server. Those powers that be do not agree. Here are two solutions:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path])-7) END) AS PathToDirector
Or:
(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path] + '1234567')-7) END AS PathToDirector
You may have the same problem with the CHARINDEX().
The use of a where condition related to column from left join table generate and implicit inner join
In this case add the condition to the ON clause for let the left join work
SELECT
PersonTable.FullName,
View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
AND View_PersonToDirector.DirectorId = 12345
for the length problem try use conditional eg using case
SELECT
items.PersonId,
items.[Level],
case when LEN(items.[Path]) > 7 then RIGHT(items.[Path], LEN(items.[Path])-7)
ELSE items.[Path] END AS PathToDirector
FROM items
The base part of your rCTE has:
CAST(PersonId AS VARCHAR(255)) AS [Path]
Which means base rows of the rCTE would contain values such as 1234 which are shorter than 7 characters and cause RIGHT(x, LEN(x) - 7) to fail. The condition:
RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
Could be safely written as:
SUBSTRING(items.[Path], NULLIF(CHARINDEX(' < ', items.[Path]), 0) + 3, LEN(items.[Path]))
I want to replace In with Like to make the query work.
SELECT
1 AS coddit, COD_CAT AS cam_cod, DES_CAT AS cam_desc,
LIVELLO_CAT AS livello, COD_CAT_PADRE AS cat_padre,
COD_L1, COD_L2, COD_L3, COD_L4, COD_L5, COD_L6
FROM
dbo.CLASS_ART
WHERE
1=1
AND TIPO_CLASS = 16 --B2B
AND LIVELLO_CAT = '0'
AND COD_CAT IN (SELECT DISTINCT CAT_MERCE.COD_CAT
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002')
The comparison I would like to do with LIKE otherwise the query doesn't work well
the subquery returns more than one value and it is correct but if I use Like instead of IN I have this error message:
Query return more than 1 values
Using LIKE against a subquery that returns multiple records won't work. A solution would be to turn the IN condition to an EXISTS condition, like:
and exists (
select 1
from ART_LIST_PREZZI
inner join ART_ANA
on ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
inner join CAT_MERCE
on ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
and ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
where COD_CAT like '%' + CAT_MERCE.COD_CAT + '%'
)
Like has to be compared to a single string, so you need to set all your ids on a single string. You can do that using the for xml clause.
(SELECT DISTINCT CAST(CAT_MERCE.COD_CAT AS VARCHAR(32))
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
FOR XML PATH(''))
Now I would delimite your ids by commas, so you don't find false positives, and compare it using like.
AND
(SELECT DISTINCT ',' + CAST(CAT_MERCE.COD_CAT AS VARCHAR(32)) + ','
FROM ART_LIST_PREZZI
INNER JOIN ART_ANA ON ART_LIST_PREZZI.COD_ART = ART_ANA.COD_ART
INNER JOIN CAT_MERCE ON ART_ANA.COD_CAT = CAT_MERCE.COD_CAT
AND ART_LIST_PREZZI.COD_LIST = 'EXPORT_002'
FOR XML PATH('')) LIKE '%,' + COD_CAT + ',%'
This would work, and you would have changed your IN operator with a LIKE operator, but I don't see the point of it, its performance would be worse than your original query.
Use EXISTS:
EXISTS (SELECT 1
FROM ART_LIST_PREZZI LP JOIN
ART_ANA A
ON LP.COD_ART = A.COD_ART JOIN
CAT_MERCE M
ON A.COD_CAT = M.COD_CAT AND
LP.COD_LIST = 'EXPORT_002' AND
CLASS_ART.COD_CAT LIKE M.COD_CAT
)
I assume that the logic you actually want uses wildcards:
CLASS_ART.COD_CAT LIKE CONCAT('%', M.COD_CAT, '%')
If so, it suggests an issue with the data model. Why would two columns with the same name (COD_CAT) need to be joined using LIKE instead of =.
I am writing select statement in SQL Server. I have to add a select query.
select
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID) as Reviewers
from tblUserAccount acct
I need to get more names from a table called tblReviwers. So 1 user from the tblUserAccount table could be associated with multiple reviews.
The tblReviewers only has 3 column AnalystID, ReviewerID, and Date. When I select on the JOIN between TblUserAccount and TblReviewers on a test AccountID = AnalystID, I can get multiple ReviewerIDs, which their firstname and lastname are located in the tblUserAccount. This is the reason why I use a select subquery
When I run the query, I get the following error
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
I am trying to write a VIEW to get a data.
Any help is greatly appreciated.
A query for a column name can only contain a single record result set. If you have an entry with multiple results causes that error.
I think you are missing disclosure of a possible extra component needed in the reviewers table, the who entered it. This will result in a query similar to the following.
The aliases and relations will appear obvious, but need to be confirmed for your actual structure.
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
ReviewBy.FirstNm + ' ' + ReviewBy.LastNm as ReviewerName
from
tblReviewers r
tblUserAccount EnterBy
on r.AccountID = EnterBy.AccountID
tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
REVISION BASED ON ADDITIONAL DATA
Based on providing the analystID on who entered, you should be good with
select
EnterBy.AccountID,
EnterBy.Username,
EnterBy.LastNm + ', ' + EnterBy.FirstNm as Name ,
EnterBy.Lastlogin,
EnterBy.email as Email,
STUFF(( Select
', [' + ReviewBy.LastNm + ', ' + ReviewBy.FirstNm + '] ' AS [text()]
From
tblReviewers r
JOIN tblUserAccount ReviewBy
on r.ReviwerID = ReviewBy.AccountID
where
r.AnaylstID = EnterBy.AccountID
For XML PATH('')), 1, 2, '' ) as AllReviewers
from
tblUserAccount EnterBy
You cannot have a column that contains multiple rows in a single result set, it just doesn't make sense from SQL perspective. Hence the error.
You should JOIN the tblReviewers table instead of subselecting it. That will yield all Reviewers. Then, JOIN again on the tblUserAccount table to get the name of the Reviewer, and you are all set.
SELECT
acct.AccountID,
acct.Username,
acct.LastNm + ', ' + acct.FirstNm as Name ,
acct.Lastlogin,
acct.email as Email,
acct2.LastNm + ', ' + acct2.FirstNm as ReviewerName ,
FROM tblUserAccount acct
JOIN tblReviewers revi
ON on acct.AccountID = revi.ReviewerID
JOIN tblUserAccount acct2
ON on acct2.AccountID = revi.AnalystID
Starting from Sql Server 2017, You can use something like STRING_AGG function, which combines values from multiple rows into a single string.
Then, your Reviewers column in your query might look like this:
(select STRING_AGG(Name, ',') from
(select acct2.FirstNm + ' ' + acct2.LastNm as Name from tblUserAccount acct2
join tblReviewers ra2 on acct2.AccountID = ra2.ReviwerID
where ra2.AnalystID = acct.AccountID)
) as Reviewers
In this case, names(first name + last name) of the reviewers for the current user will be separated by commas.
Subselects in select lists can only be used to return one value.
In your case, use joins
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm as Name ,
a.Lastlogin,
a.email as Email,
b.LastNm + ', ' + b.FirstNm as ReviewerName
FROM
tblUserAccount a
LEFT JOIN tblReviewers r ON a.AccountID = r.AnalystID
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
I also used a LEFT JOIN to the reviewers table, in case there is no reviewer defined for an analyst.
You will get several rows per analyst, if more than one reviewer is assigned to it. Usually I solve this problem in the front-end by making a report that puts the main data in a group header (the analyst) and the related date (the reviewers) in the detail section of the report.
Starting with SQL Server 2017, you can also use the STRING_AGG function to concatenate the values of string expressions.
SELECT
a.AccountID,
a.Username,
a.LastNm + ', ' + a.FirstNm AS Name ,
a.Lastlogin,
a.email AS Email,
STRING_AGG(
( SELECT b.LastNm + ' ' + b.FirstNm
FROM
tblReviewers
INNER JOIN tblUserAccount b ON r.ReviewerID = b.AccountID
WHERE
r.AnalystID = a.AccountID ),
', ') AS ReviewerName
FROM
tblUserAccount a
This allows you to return more than one reviewer in one field.
I have added a new column to a table and want to prepopulate it.
At the moment I have this:
update Table_A
set packages = 'a:1:{i:0;a:2:{s:4:"name"; s:' + ltrim(str(len(p.name))) + ':"' + p.name + '";'+
's:11:"category_id"; a:1:{i:0;s:1:"0";}}}'
from broker_product as Table_A
join scheme s on Table_A.scheme_id = s.id
join product p on p.id = s.product_ids
where Table_A.packages is null
What I want to do is populate category_id with a value based on the values of a column in product, as opposed to the fixed string that it is now.
An example of what is in product.risks is:
a:2:{i:0;a:5:{s:10:"field_name";s:24:"Gadget Accidental Damage";s:19:"risk_category_value";s:4:"1.00";s:13:"variable_name";s:24:"gadget-accidental-damage";s:11:"description";s:0:"";s:10:"hover_text";s:0:"";}i:1;a:5:{s:10:"field_name";s:32:"Home Emergency Accidental Damage";s:19:"risk_category_value";s:4:"2.00";s:13:"variable_name";s:0:"";s:11:"description";s:0:"";s:10:"hover_text";s:0:"";}}
In this example I would like category_id to be:
a:2:{i:0;s:1:"0";i:1;s:1:"1";}
which is an array of the same length as products.risk, with the values i:0;s:1:"0"; for the size of the array, counting the number of elements in product.risks.
As an example, I am looking for something like this (with correct regex, not the broken stuff I've supplied):
update Table_A
set packages = 'a:1:{i:0;a:2:{s:4:"name"; s:' + ltrim(str(len(p.name))) + ':"' + p.name + '";'
's:11:"category_id"; ' + preg_replace('/a:(\d+):{(i:\d+;)/', 'a:$1:{$2s:1:"0";}', p.risks) + '}}'
from broker_product as Table_A
join scheme s on Table_A.scheme_id = s.id
join product p on p.id = s.product_ids
where Table_A.packages is null
I have two table with the following data:
TableA.name
R4.23-Core-2
R4.23-Core-2
LA#213 CGHPBXsw01 127.213 0024-737e-e341
LA#252 CGHRack1sw01 127.252 0022-57ab-d781
SOC-01A-SW01
to - R4-DISTR-9512
to-R2-DISTR-5900-1
to-R3.25-EDGE
TableB.caption
R4.23-Core-2.ehd.ca
R4.23-Core-2.nhd.ca
CGHPBXsw01
CGHRack1sw01
SOC-01A-SW01
R4-DISTR-9512
R2-DISTR-5900-1.phsnc.
R3.25-EDGE.phsne.edjc.ca
I've tried using the following join statement but it doesn't seem to work for any row with a . in it.
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON dbo.TableA.Name LIKE '%' + dbo.TableB.Caption + '%'
I also try using replace function, which work but there are too much variant to include with replace.
I could try using the RIGHT or LEFT function to normalize the data but for row that doesn't have '.' it would throw an error. And I don't know how to skip row that doesn't have '.'
What is the most efficient way to join these two table?
In some situations in your example the caption is longer, and other times the name is longer, if you wanted to join on any value where name is in the caption or caption is in the name you could use:
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON dbo.TableA.Name LIKE '%' + dbo.TableB.Caption + '%'
OR dbo.TableB.Caption LIKE '%' + dbo.TableA.Name + '%'
That could explain why your query isn't working as expected.
As far as the most efficient way to do this, you'd want to have a standardized field in your table that you could use to JOIN on via equality (ex. a.col1 = b.col1), so that would entail stripping out the heart of each field that makes it join-worthy.
Update: If the important part is everything before the first period, then you want to use a combination of LEFT() and CHARINDEX() (and a CASE statement since not all strings contain a period):
SELECT NewField = CASE WHEN CHARINDEX('.',Name) > 0 THEN LEFT(Name,CHARINDEX('.',Name)-1)
ELSE Name
END
FROM YourTable
You could use the above in your JOIN too:
dbo.TableA.Name
INNER JOIN dbo.TableB.Caption
ON CASE WHEN CHARINDEX('.',TableA.Name) > 0 THEN LEFT(TableA.Name,CHARINDEX('.',TableA.Name)-1)
ELSE TableA.Name
END
= CASE WHEN CHARINDEX('.',TableB.Caption) > 0 THEN LEFT(TableB.Caption,CHARINDEX('.',TableB.Caption)-1)
ELSE TableB.Caption
END
How about this ( Not Tested)
dbo.TableA
INNER JOIN dbo.TableB
ON CHARINDEX(dbo.TableB.Caption, dbo.TableA.Name) > 0
Test it and don't forget Upvote OR accept.