SQL - String Logic - sql

I don't know if what I am wanting to achieve is possible so here is my conundrum.
Within a SQL table there are a number of fields that contain yes/no flags in a string so for example. On the field may be be called 'Stock' and within this one field there is a string of flags which e.g. 'YNYYY' lets say for example that the flags stand for.
Coke
Fanta
Pepsi
Lilt
Dr Pepper
in this instance I would want in my return of data to return Coke,Pepsi,Lilt,Dr Pepper ommiting the Fanta.
Now this would be possible using the CASE Statement and this may be the answer that I have to use, however ideally so I don't have to write hundreds of different variables anyone know of a way this could be achieved?
Your help as always appreciated, I've done the normal googling and maybe I simply don't know what to search for as its giving me blanks.
Please point me in the right direction.
Regards
R

Why dont you use SELECT with WHERE?
Something like this.
SELECT GROUP_CONCAT(`Stock`)
FROM table_name
WHERE `flag` = 'Y'
Hope this helps.

One way you can achieve your goal is by writing a table valued function that turns your Y/N string into a table of (Id INT, value BIT), you could then join to a look up table based on convention. Here's what something like this would look like:
CREATE FUNCTION udf_StringToBool(#intput varchar(100))
RETURNS #table TABLE (
Id INT IDENTITY(1,1),
Value BIT
)
AS
begin
declare #temp_input varchar(100) = #intput
while len(#temp_input) > 0
begin
insert into #table (value)
SELECT CASE LEFT(#temp_input, 1) WHEN 'Y' THEN 1 ELSE 0 END
set #temp_input = right(#temp_input, len(#temp_input)-1)
END
RETURN
end
You would then join your stock (and a lookup to product) table with this function, then remove any that are not in stock in the WHERE clause:
SELECT s.*, v.Value, pl.Name
FROM
stock s
cross apply
(
select b.* from udf_StringToBool(s.Flags) b
) v
join product_lookup pl on pl.Id = v.Id
WHERE v.Value = 1
Here's how you would define the lookup table:
create table product_lookup
(
Id INT IDENTITY(1,1),
Name Varchar(50)
)
insert into product_lookup (Name) values
('Coke'),
('Fanta'),
('Pepsi'),
('Lilt'),
('Dr Pepper')
Then you could use PIVOT to generate the columns with booleans.

In the end I chose to use 'substring' and the 'case' statement so that each item appeared in it's own field this way I mitigated the need to write every variable.
SELECT CASE WHEN SUBSTRING(STOCK,1,1) = 'y' THEN 'IN STOCK' ELSE 'OUTOFSTOCK' END AS COKE
I don't know why it didnt occur to me to begin with and without your prompting and guidance I would have probably done this the long way round as ever thanks to all!

Related

T-SQL Skip cte Table and Union when Variable Is Null

I've got a query that captures all shipments and costs from our factory. Sample data and desired output on Google Drive here:
https://drive.google.com/open?id=0B4xdnV0LFZI1VndEaGgxNDVpU2M
The issue is we've got 2 different ways of selling things. One is 'Regluar' where we make it and the other one is a 'buy/sell' where we buy and sell it.
To capture the costs I've had to write two queries, one for each scenario. The end users of this query can enter in a date range and the query works well then, but I'm stuck when it comes to the variable #Job_No.
All work that goes through our factory (cteRegularJobs) has a Job Number associated with it and I've declared a variable so users can use it to search. The cteBuyandSell has a value of 'NULL' for Job_No declared in the SELECT statement so I can do a UNION of these two tables at the end. However, no buy/sell jobs have Job Numbers assigned to them, they are always NULL.
Initially #Job_No is declared as '' and when it's left '' I want the results from both cte tables returned. If there is an entry by the user, i.e. '001' then I want results for cteRegularJobs.
If it makes it easier I am open to declaring the cteBuyandSell.Job_No something else besides NULL, like "Buy and Sell".
The real query is complicated so here's a simplified example of the structure:
DELCARE #Job_No AS varchar(10) = '';
SET #Job_No = {User Input or leave as ''};
WITH
cteBuyandSell AS ( NULL AS 'Job_No',
...),
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT *
FROM
(cteBuyandSell
UNION
cteRegularJobs)
You can logically break this up with an IF statement to check the value of your variable. I'd suggest NULL over white space though. Here's an example procedure... with the limited code you provided.
CREATE PROCEDURE getData(#Job_No varchar(10) = NULL)
AS
IF #Job_No IS NULL
BEGIN
WITH
cteBuyandSell AS ( NULL AS 'Job_No',
...),
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT *
FROM
(cteBuyandSell
UNION
cteRegularJobs)
END
ELSE
BEGIN
WITH
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT * FROM cteRetularJobs
END

How to write a row that is empty to a Table Variable

I have the following SQL query
Declare #EIDDetail Table
(inc_synopsis varchar(5000),EmployeeName varChar(50),inc_id int, acc_id int
,acc_eid int, inc_event_Number Varchar(12), inc_date_occurred_startdate,acc_afg_id int,
inc_time_occurred_start varchar(8),inc_date_recvd date,inc_date_closed date,inc_is_uof bit,
InvestigatorName VarChar(42),inc_is_comp_via_sup bit,
inc_is_comp_via_psdbit, inc_is_admin_review bit
, inc_comp_is_inquiry bit, inc_comp_is_invest bit, div_name VarChar(50), inc_comp_is_referral bit)
INSERT INTO #EIDDetail SELECT b.inc_synopsis, a.EmployeeName As AccusedName, b.inc_id, a.acc_id, a.acc_eid,
b.inc_event_number, b.inc_date_occurred_start, a.acc_afg_id, b.inc_time_occurred_start, b.inc_date_recvd, b.inc_date_closed, b.inc_is_uof,
c.InvestigatorName, b.inc_is_comp_via_sup, b.inc_is_comp_via_psd,b.inc_is_admin_review,b.inc_comp_is_inquiry,b.inc_comp_is_invest, d.div_name,
b.inc_comp_is_referral
FROM dbo.VW_ACCUSED_DISCIPLINABLE_CHARGE AS a INNER JOIN
dbo.Tbl_Incident AS b ON a.acc_inc_id = b.inc_id LEFT OUTER JOIN
dbo.VW_INCIDENT_INVESTIGATOR AS c ON b.inc_id = c.inc_id LEFT OUTER JOIN
dbo.Tbl_Division AS d ON b.inc_inv_div_id = d.div_id
WHERE
a.acc_eid IN (435,35) And (b.inc_comp_is_inquiry = 'False') AND (b.inc_deleted = 'False') OR
a.acc_eid IN (435,35) And(b.inc_deleted = 'False') AND (b.inc_comp_is_invest = 'False') OR
a.acc_eid IN (435,35) And(b.inc_deleted = 'False') AND (b.inc_comp_is_referral = 'False') OR
a.acc_eid IN (435,35) And(b.inc_deleted = 'False') AND (b.inc_is_uof = 'True')
select * from #EIDDetail
This works okay until I have a parameter that has no records. When That Happens I need to include that employee identified by the parameter and show in the record set the employee name and "No Records Found' in my synopsis field.
I can not seem to figure this out. I have tried using the ISNULL function like this
SELECT ISNULL((SELECT My code above)), (new select here))
But that gives me an error message "Only one expression can be specified in the select list when the subquery is not introduced with EXISTS."
Am I heading in the right direction with the ISNUll function or is there any other way to accomplish this
I'd do something like:
DECLARE #EIDDetail Table <etc>
INSERT INTO #EIDDetail
SELECT <etc>
IF ##rowcount = 0
INSERT INTO #EIDDetail
SELECT <query designed for "no data available" parameters>
Just because it is possible to jam everything into a single query (something I am guilty of myself) doesn't make it a good idea.
If you want to use the ISNULL expression, then you have to use it on that one particular field (not a whole select statement). So, you could have something like:
SELECT ISNULL(field1, "Empty") AS field1, ISNULL(field2, "Empty") AS field 2
FROM TableName
The above assumes that Field1 and field2 are string-types (e.g., Varchar).
The output of your query then should be a left join or outer apply where your primary table is the list of IDs that you passed in (either in flex parameter or a table variable form), so:
SELECT IDs.ID, ISNULL(Records.A, "No Data Available")[, ...n]
FROM #TableOfIDs IDs
OUTER APPLY (
<query returning results you are interested in>
WHERE IDs.ID = InnerIDTable.ID
) Records
Then, on the report side, make your outputs responsive to the first occurrence of "No Data Available" so that it reformats the output. As the point above is made, ensure that your output from ISNULL is the same type as the column you're selecting.

SQL - Retrieve records based on parameters where either parameter can be null

I have a stored procedure which takes in five parameters of which two can be null - we will call these parameters A and B
What I would like to do is select records based on the following logic.
If Parameter A is NULL then only return records that match Parameter B
I know that I can do something similar to the following
IF A IS NULL
BEGIN
SELECT * FROM TABLE WHERE Param=B
END
ELSE
BEGIN
SELECT * FROM TABLE WHERE Param=A
END
However, the SQL query is much more complex then the above one and there would be huge replication in the Proc which is something I want to avoid
Thanks in advance
===============================
EDIT - Sorry, I should have mentioned that in the example the Param are based on separate columns e.g.
My table consists of four columns of which two separate columns map to the two parameters - basic schema below
ID
PersonName
GroupID
DeliveryID
In my procedure I want to retrieve those records that match the GroupID however in the scenario where the GroupID is null then I want to return those records that match the DeliveryID
Thanks again
Try
SELECT * FROM my_table WHERE Param = COALESCE(A,B)
COALESCE will give you A if it's not null. Otherwise B.
Functionally, something like this should work. If either parameter is NULL, the condition becomes a self-identity (assuming neither groupID nor deliveryID is NULL).
SELECT *
FROM table_name
WHERE groupID = coalesce(#groupIDParameter, groupID)
AND deliveryID = coalesce(#deliveryIDParameter, deliveryID)
Try ISNULL function:
SELECT * FROM TABLE WHERE Param = ISNULL(B,A)
You could also use a case statement Case when A is Null Then B

Combine Unique Column Values Into One to Avoid Duplicates

For simplicity, assume I have two tables joined by account#. The second table has two columns, id and comment. Each account could have one or more comments and each unique comment has a unique id.
I need to write a t-sql query to generate one row for each account - which I assume means I need to combine as many comments as might exit for each account. This assumes the result set will only show the account# once. Simple?
Sql Server is a RDBMS best tuned for storing data and retrieving data, you can retrieve the desired data with one very simple query but the desired format should be handled with any of the reporting tools available like ssrs or crystal reports
Your query will be a simple inner join something like this
SELECT A.Account , B.Comment
FROM TableA AS A INNER JOIN TableB AS B
ON A.Account = B.Account
Now you can use your reporting tool to Group all the Comments by Account when Displaying data.
I do agree with M. Ali, but if you don't have that option, the following will work.
SELECT [accountID]
, [name]
, (SELECT CAST(Comment + ', ' AS VARCHAR(MAX))
FROM [comments]
WHERE (accountID = accounts.accountID)
FOR XML PATH ('')
) AS Comments
FROM accounts
SQL Fiddle
In my actual project I have this exact situation.
What you need is a solution to aggregate the comments in order to show only one line per account#.
I solve it by creating a function to concatenate the comments, like this:
create function dbo.aggregateComments( #accountId integer, #separator varchar( 5 ) )
as
begin;
declare #comments varchar( max ); set #comments = '';
select #comments = #comments + #separator + YouCommentsTableName.CommentColumn
from dbo.YouCommentsTableNAme
where YouCommentsTableName.AccountId = #accountId;
return #comments;
end;
You can use it on you query this way:
select account#, dbo.aggretateComments( account#, ',' )
from dbo.YourAccountTableName
Creating a function will give you a common place to retrieve your comments. It's a good programming practice.

SQL Replace Multiple Values based on one column value

I need to replace results on up to 9 columns based on one other column value.
If the column "role" is the value "SeniorManagement", I want to replace what would be default hierarchical values with hard values.
Here's how I can accomplish this now:
, CASE
WHEN d.Role = 'SeniorManagement'
THEN (Replace(p.Firstname,'John','Joe')) END as First
, CASE
WHEN d.Role = 'SeniorManagement'
THEN (Replace(p.Lastname,'TopDog','AssignedPerson')) END as Last
, CASE...
My question is, is there a less verbose way to combine these?
Pseudo ( I know this doens't work :-)
, CASE
WHEN d.Role = 'SeniorManagement'
THEN (Replace(p.Firstname,'John','Joe')) as First
THEN (Replace(p.Lastname,'TopDog','AssignedPerson')) as Last
THEN (Replace(p.Email,'TopDog#wherever','AssignedPerson#wherever')) as Email
Thanks
what about using a function?
create function dbo.GetColumnValue
(
#ColumnBasedValue varchar(max),
#ColumnToReplace varchar(max), --or the type you want
#ToReplace varchar(max),
#Replacement varchar(max)
)
as
BEGIN
return (select case #ColumnBasedValue when 'SeniorManagement'
then (Replace(#ColumnToReplace,#ToReplace,#Replacement))
else #replaceValue END)
END
and use it like
select dbo.GetColumnValue(d.Role, p.Firstname,'John','Joe') as First,
dbo.GetColumnValue(d.Role, p.Lastname,'TopDog','AssignedPerson') as Last..
Depending on what the context is, you might be able to get away with doing raw selects.
Select d.Role, 'Joe' as First, 'AssignedPerson' as Last
and just filter each select with a where clause.
If you want to do multiple ones, use a union.