SQL CONCAT IF Statement? - sql

Morning All,
Im not to sure how i need to solve my following query... I have the following query which pulls back the desired records in SQL server...
SELECT agenda.AgendaItemNumber,Agenda.AgendaName, AgendaType.AgendaTypeDescription, userdetails.fullName
FROM Agenda
JOIN AgendaType ON AgendaType.AgendaTypeID=Agenda.AgendaTypeID
JOIN UserDetails ON Agenda.AgendaID = Userdetails.AgendaID
WHERE agenda.AgendaTypeID = '2'
AND AgendaItemNumber = AgendaItemNumber
AND AgendaName = AgendaName
AND AgendaTypeDescription = AgendaTypeDescription
AND AgendaItemNumber >= '3'
The above query works but i need to enhance this slightly. It pulls back the following results, which essentially are duplicate records except for the 'fullname' column...
What i would like to do is be able to add some extra code to this query so that when i run the query i am able to display one record for each 'AgendaItemNumber' and for it to concat both of the fullnames for this record. However i have additional AgendaItemsNumbers in this table that only have 1 x user fullname assigned to them. its just these few records within the image file i need to do something clever with.
Maybe there is a better way to complete this task?
Many thanks in advance. Any queries please dont hesitate to ask.
Regards
Betty

SELECT agenda.AgendaItemNumber,
Agenda.AgendaName,
AgendaType.AgendaTypeDescription,
STUFF(( SELECT ';' + FullName
FROM UserDetails
WHERE UserDetails.AgendaID = Agenda.AgendaID
FOR XML PATH('')
), 1, 1, '') AS fullName
FROM Agenda
INNER JOIN AgendaType
ON AgendaType.AgendaTypeID=Agenda.AgendaTypeID
INNER JOIN UserDetails
ON Agenda.AgendaID = Userdetails.AgendaID
WHERE agenda.AgendaTypeID = '2'
AND AgendaItemNumber = AgendaItemNumber
AND AgendaName = AgendaName
AND AgendaTypeDescription = AgendaTypeDescription
AND AgendaItemNumber >= '3'
ADENDUM
The XML extension in SQL-Server allows you to concatenate multiple rows into a single row. The actual intention of the extension is so you can output as XML (obviously), but there are some nifty tricks that are byproducts of the extensions. In the above query, if there were a column name in the subquery (FullName) it would output as <FullName>Joe Bloggs1</FullName><FullName>Joe Bloggs2</FullName>, because there is no column name it simply concatenates the rows (not forming proper XML). The PATH part allows you to specify an additional node, for example if you use PATH('Name') in the above you would get <Name>;Joe Bloggs</Name><Name>;Joe Bloggs2</Name> If you combine Path with a column name you would get Joe Bloggs.
Finally the STUFF just removes the semicolon at the start of the list.

Related

How to write an Open SQL statement with substring in the JOIN ON condition? [duplicate]

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Select all columns from one table and one from another where column equals variable

Sorry for the long title.
I have a statement which needs to grab all the columns from one row from BinConfig:
SELECT *
FROM BinConfig WITH(NOLOCK)
WHERE IssuerKey = #IssuerKey
But I also need to grab a single column from one row from CardRangeGroup also based on that IssuerKey column.
What I've tried:
SELECT
BinConfig.*, CardRangeGroup.Name
FROM
BinConfig
JOIN
CardRangeGroup WITH(NOLOCK)
WHERE
#IssuerKey = BinConfig.IssuerKey
AND #IssuerKey = CardRangeGroup.IssuerKey
Which gives me a syntax error near WHERE. I've tried to find resources online, but everywhere I look I can't find anything explaining how to select rows based on a passed in variable. Any help?
You need to specify how the tables should be joined. Try this:
SELECT BinConfig.*, CardRangeGroup.Name
FROM BinConfig
JOIN CardRangeGroup ON BinConfig.IssuerKey = CardRangeGroup.IssuerKey
WHERE #IssuerKey = CardRangeGroup.IssuerKey
The with(nolock) might not be needed (or a good idea) so I removed it.
try this , you don't need to use where
SELECT BinConfig.*, CardRangeGroup.Name FROM BinConfig JOIN
CardRangeGroup
ON CardRangeGroup.IssuerKey = BinConfig.IssuerKey AND #IssuerKey = CardRangeGroup.IssuerKey

Using the results of a select sub query as the columns to select in the main query. Injection?

I have a table that contains a column storing sql functions, column names and similar snippets such as below:
ID | Columsql
1 | c.clientname
2 | CONVERT(VARCHAR(10),c.DOB,103)
The reason for this is to use selected rows to dynamically create results from the main query that match spreadsheet templates. EG Template 1 requires the above client name and DOB.
My Subquery is:
select columnsql from CSVColumns cc
left join Templatecolumns ct on cc.id = ct.CSVColumnId
where ct.TemplateId = 1
order by ct.columnposition
The results of this query are 2 rows of text:
c.clientname
CONVERT(VARCHAR(10),c.DOB,103)
I would wish to pass these into my main statement so it would read initially
Select(
select columnsql from CSVColumns cc
left join Templatecolumns ct on cc.id = ct.CSVColumnId
where ct.TemplateId = 1
order by ct.columnposition
) from Clients c
but perform:
select c.clientname, CONVERT(VARCHAR(10),c.DOB,103) from clients c
to present a results set of client names and DOBs.
So far my attempts at 'injecting' are fruitless. Any suggestions?
You can't do this, at least not directly. What you have to do is, in a stored procedure, build up a varchar/string containing a complete SQL statement; you can execute that string.
declare #convCommand varchar(50);
-- some sql to get 'convert(varchar(10), c.DOB, 103) into #convCommand.
declare #fullSql varchar(1000);
#fullSql = 'select c.clientname, ' + #convCommand + ' from c,ients c;';
exec #fullSql
However, that's not the most efficient way to run it - and when you already know what fragment you need to put into it, why don't you just write the statement?
I think the reason you can't do that is that SQL Injection is a dangerous thing. (If you don't know why please do some research!) Having got a dangerous string into a table - e.g 'c.dob from clients c;drop table clients;'- using the column that contains the data to actually execute code would not be a good thing!
EDIT 1:
The original programmer is likely using a C# function:
string newSql = string.format("select c.clientname, {0} from clients c", "convert...");
Basic format is:
string.format("hhh {0} ggg{1}.....{n}, s0, s1,....sn);
{0} in the first string is replaced by the string at s0; {1} is replaces by tge string at s1, .... {n} by the string at sn.
This is probably a reasonable way to do it, though why is needs all the fragments is a bit opaque. You can't duplicate that in sql, save by doing what I suggest above. (SQL doesn't have anything like the same string.format function.)

How can I run a SQL query iteratively for every row in a table?

I have the following query:
DECLARE #AccString varchar(max)
SET #AccString=''
SELECT #Acctring=#AccString + description + ' [ ] '
FROM tl_sb_accessoryInventory ai
JOIN tl_sb_accessory a on a.accessoryID = ai.accessoryID
WHERE userID=6
SELECT userID, serviceTag, model, #AccString AS ACCESSORIES FROM tl_sb_oldLaptop ol
JOIN tl_sb_laptopType lt ON ol.laptopTypeID = lt.laptopTypeID
WHERE userID=6
which outputs this:
What I want to be able to do is run this for every userID in a table tl_sb_user.
The statement to get the userIDs is:
Select userID from tl_sb_user
How can I get this to output a row as above for each user?
You are trying to do a string concatenation subquery. In SQL Server, you need to do the string concatenation using a correlated subquery with for xml path. Arcane, but it generally works.
The results is something like this:
SELECT userID, serviceTag, model, #AccString AS ACCESSORIES,
stuff((select ' [ ] ' + description
from tl_sb_accessoryInventory ai join
tl_sb_accessory a
on a.accessoryID = ai.accessoryID
where a.userId = ol.UserId
for xml path ('')
), 1, 11, '') as accessories
FROM tl_sb_oldLaptop ol JOIN
tl_sb_laptopType lt
ON ol.laptopTypeID = lt.laptopTypeID;
You don't have table aliases identifying where the columns come from, so I am just guessing that a.userId = ol.UserId references the right tables.
Also, this substitutes certain characters with html forms. Notably '<' and '>' turn into things like '<' and '>'. When I encounter this problem, I use replace() to replace the values.
Simply leave out the WHERE clause.

naming columns in excel with Complex sql

I’m trying to run this SQL using get external.
It works, but when I try to rename the sub-queries or anything for that matter it remove it.
I tried as, as and the name in '', as then the name in "",
and the same with space. What is the right way to do that?
Relevant SQL:
SELECT list_name, app_name,
(SELECT fname + ' ' + lname
FROM dbo.d_agent_define map
WHERE map.agent_id = tac.agent_id) as agent_login,
input, CONVERT(varchar,DATEADD(ss,TAC_BEG_tstamp,'01/01/1970'))
FROM dbo.maps_report_list list
JOIN dbo.report_tac_agent tac ON (tac.list_id = list.list_id)
WHERE input = 'SYS_ERR'
AND app_name = 'CHARLOTT'
AND convert(VARCHAR,DATEADD(ss,day_tstamp,'01/01/1970'),101) = '09/10/2008'
AND list_name LIKE 'NRBAD%'
ORDER BY agent_login,CONVERT(VARCHAR,DATEADD(ss,TAC_BEG_tstamp,'01/01/1970'))
You could get rid of your dbo.d_agent_define subquery and just add in a join to the agent define table.
Would this code work?
select list_name, app_name,
map.fname + ' ' + map.lname as agent_login,
input,
convert(varchar,dateadd(ss,TAC_BEG_tstamp,'01/01/1970')) as tac_seconds
from dbo.maps_report_list list
join dbo.report_tac_agent tac
on (tac.list_id = list.list_id)
join dbo.d_agent_define map
on (map.agent_id = tac.agent_id)
where input = 'SYS_ERR'
and app_name = 'CHARLOTT'
and convert(varchar,dateadd(ss,day_tstamp,'01/01/1970'),101) = '09/10/2008'
and list_name LIKE 'NRBAD%'
order by agent_login,convert(varchar,dateadd(ss,TAC_BEG_tstamp,'01/01/1970'))
Note that I named your dateadd column because it did not have a name. I also tried to keep your convention of how you do a join. There are a few things that I would do different with this query to make it more readable, but I only focused on getting rid of the subquery problem.
I did not do this, but I would recommend that you qualify all of your columns with the table from which you are getting them.
To remove the sub query in the SELECT statement I suggest the following:
SELECT list_name, app_name, map.fname + ' ' + map.lname as agent_login, input, convert(varchar,dateadd(ss, TAC_BEG_tstamp, '01/01/1970))
FROM dbo.maps_report_list inner join
(dbo.report_tac_agent as tac inner join dbo.d_agent_define as map ON (tac.agent_id=map.agent_id)) ON list.list_id = tac.list_id
WHERE input = 'SYS_ERR' and app_name = 'CHARLOTT' and convert(varchar,dateadd(ss,day_tstamp,'01/01/1970'),101) = '09/10/2008'
and list_name LIKE 'NRBAD%' order by agent_login,convert(varchar,dateadd(ss,TAC_BEG_tstamp,'01/01/1970'))
I used parentheses to create the inner join between dbo.report_tac_agent and dbo.d_agent_define first. This is now a set of join data.
The combination of those tables are then joined to your list table, which I am assuming is the driving table here. If I am understand what you are trying to do with your sub select, this should work for you.
As stated by the other poster you should use table names on your columns (e.g. map.fname), it just makes things easy to understand. I didn't in my example because I am note 100% sure which columns go with which tables. Please let me know if this doesn't do it for you and how the data it returns is wrong. That will make it easier to solve in needed.