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

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.

Related

SQL - Stripping a string and using it in a condition

So I have a SQL query issue given to me which i'm struggling to resolve:
It currently brings back 6710445 rows but i need to apply further conditions based on a particular string field.
SELECT
Table1.ExampleColumn1 -- (ID)
,Table1.ExampleColumn2
,Table2.ExampleColumn3
,Table2.ExampleColumn4
,Table3.ExampleColumn5
,Table3.ExampleColumn6
,Table1.StringField
FROM [Example Database].[dbo].[Table1] AS Table1
INNER JOIN [Example Database].[dbo].[Table2] AS Table2
ON Example = Example
INNER JOIN [Example Database].[dbo].[Table3] AS Table3
ON Example = Example
WHERE Month BETWEEN 201304 AND 201603
AND (Age < 19)
The above 'Table1.StringField' has the following type codes displayed as a string in each the rows: "||J183,Y752,J374,Y752."
I also have a reference table (Call it 'Ref1') with 514 of these codes displayed individually, which has no other fields in the table whatsoever.
So what i need to be able to do is find rows from the query above which has any of values from the 'Ref1' displayed anywhere within 'Table1.StringField' individual rows, and if not to not include that row in the results set.
I tried to strip down the 'StringField' column of the comma's and "||" but it didn't work as well as i hoped and ended up bringing back over 30M rows.
Any ideas on how to do this? Preferably so it's efficient and doesn't make the user wait 10 minutes just to query it?
Maybe this will get you half way there... I also agree with Sean Lange's comment about not storing delimited data to begin with but I'm assuming the OP already knows this. You can also pivot/unpivot this data to achieve this as well. This is probably the most brute force way of doing sort of what you're looking to do.
--DROP TABLE #Table
--DROP TABLE #Ref
CREATE TABLE #Table (Col VARCHAR(MAX))
CREATE TABLE #Ref (Code VARCHAR(10))
INSERT INTO #Table (Col) VALUES ('A123,B234,C345'),('A123'),('C345')
INSERT INTO #Ref (Code) VALUES ('A123'),('B234')
SELECT * FROM #Table
SELECT * FROM #Ref
SELECT DISTINCT t.Col
FROM #Table t
CROSS APPLY (
SELECT CASE WHEN CHARINDEX(r.Code, t.Col) > 0 THEN 1 ELSE 0 END AS [ItsHere] FROM #Ref r) oa
WHERE oa.ItsHere = 1
What you need to do is join your query to the Ref1 table on Table1.StringField = Ref1.Ref_1_value and then exclude the Table1 rows that don't match any Ref_1_value. Like this:
SELECT
Table1.ExampleColumn1 -- (ID)
,Table1.ExampleColumn2
,Table2.ExampleColumn3
,Table2.ExampleColumn4
,Table3.ExampleColumn5
,Table3.ExampleColumn6
,Table1.StringField
FROM [Example Database].[dbo].[Table1] AS Table1
INNER JOIN [Example Database].[dbo].[Table2] AS Table2
ON Example = Example
INNER JOIN [Example Database].[dbo].[Table3] AS Table3
ON Example = Example
INNER JOIN [Example Database].[dbo].[Ref1] as Ref1
ON Table1.StringField = Ref1.Ref_1_value
WHERE Month BETWEEN 201304 AND 201603
AND (Age < 19)
AND Ref1.Ref_1_value is not null

SQL - String Logic

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!

SQL IN() operator with condition inside

I've got table with few numbers inside (or even empty): #states table (value int)
And I need to make SELECT from another table with WHERE clause by definite column.
This column's values must match one of #states numbers or if #states is empty then accept all values (like there is no WHERE condition for this column).
So I tried something like this:
select *
from dbo.tbl_docs docs
where
docs.doc_state in(iif(exists(select 1 from #states), (select value from #states), docs.doc_state))
Unfortunately iif() can't return subquery resulting dataset. I tried different variations with iif() and CASE but it wasn't successful. How to make this condition?
select *
from dbo.tbl_docs docs
where
(
(select count(*) from #states) > 0
AND
docs.doc_state in(select value from #states)
)
OR
(
(select count(*) from #states)=0
AND 1=1
)
Wouldn't a left join do?
declare #statesCount int;
select #statesCount = count(1) from #states;
select
docs.*
from dbo.tbl_docs docs
left join #states s on docs.doc_state = s.value
where s.value is not null or #statesCount = 0;
In general, whenever your query contains sub-queries, you should stop for five minutes, and think hard about whether you really need a sub-query at all.
And if you've got a server capable of doing that, in many cases it might be better to preprocess the input parameters first, or perhaps use constructs such as MS SQL's with.
select *
from dbo.tbl_docs docs
where exists (select 1 from #states where value = doc_state)
or not exists (select 1 from #state)

How to update a table if values of the attributes are contained within another table?

I've got a database like this one:
I'm trying to create a query that would enable me to update the value of the status attribute inside the incident table whenever the values of all of these three attributes: tabor_vatrogasci, tabor_policija, and tabor_hitna are contained inside the izvještaj_tabora table as a value of the oznaka_tabora attribute. If, for example, the values of the tabor_vatrogasci, tabor_policija, and tabor_hitna attributes are 3, 4 and 5 respectively, the incident table should be updated if (and only if) 3, 4, and 5 are contained inside the izvještaj_tabora table.
This is what I tried, but it didn't work:
UPDATE incident SET status='Otvoren' FROM tabor,izvjestaj_tabora
WHERE (incident.tabor_policija=tabor.oznaka
OR incident.tabor_vatrogasci=tabor.oznaka
OR incident.tabor_hitna=tabor.oznaka)
AND izvjestaj_tabora.oznaka_tabora=tabor.oznaka
AND rezultat_izvjestaja='Riješen' AND
((SELECT EXISTS(SELECT DISTINCT oznaka_tabora FROM izvjestaj_tabora)
WHERE oznaka_tabora=incident.tabor_policija) OR tabor_policija=NULL) AND
((SELECT EXISTS(SELECT DISTINCT oznaka_tabora FROM izvjestaj_tabora)
WHERE oznaka_tabora=incident.tabor_vatrogasci) OR tabor_vatrogasci=NULL) AND
((SELECT EXISTS(SELECT DISTINCT oznaka_tabora FROM izvjestaj_tabora)
WHERE oznaka_tabora=incident.tabor_hitna) OR tabor_hitna=NULL);
Does anyone have any idea on how to accomplish this?
Asuming INCIDENT.OZNAKA is the key and you need all 3 to be ralated for the event to open (I am Slovenian that why I understand ;) )
UPDATE incident
SET status='Otvoren'
WHERE oznaka in (
SELECT DISTINCT i.oznaka
FROM incident i
INNER JOIN izvještaj_tabora t1 ON i.tabor_vatrogasci = t1.oznaka_tabora
INNER JOIN izvještaj_tabora t2 ON i.tabor_policija = t2.oznaka_tabora
INNER JOIN izvještaj_tabora t3 ON i.tabor_hitna = t3.oznaka_tabora
WHERE t1.rezultat_izvjestaja='Riješen' AND t2.rezultat_izvjestaja='Riješen' AND t3.rezultat_izvjestaja='Riješen'
)
According to your description the query should look something like this:
UPDATE incident i
SET status = 'Otvoren'
WHERE (tabor_policija IS NULL OR
EXISTS (
SELECT 1 FROM izvjestaj_tabora t
WHERE t.oznaka_tabora = i.tabor_policija
)
)
AND (tabor_vatrogasci IS NULL OR
EXISTS (
SELECT 1 FROM izvjestaj_tabora t
WHERE t.oznaka_tabora = i.tabor_vatrogasci
)
)
AND (tabor_hitna IS NULL OR
EXISTS (
SELECT 1 FROM izvjestaj_tabora t
WHERE t.oznaka_tabora = i.tabor_hitna
)
)
I wonder though, why the connecting table tabor is irrelevant to the operation.
Among other things you fell victim to two widespread misconceptions:
1)
tabor_policija=NULL
This expression aways results in NULL. Since NULL is considered "unknown", if you compare it to anything, the outcome is "unknown" as well. I quote the manual on Comparison Operators:
Do not write expression = NULL because NULL is not "equal to" NULL.
(The null value represents an unknown value, and it is not known
whether two unknown values are equal.)
2)
EXISTS(SELECT DISTINCT oznaka_tabora FROM ...)
In an EXISTS semi-join SELECT items are completely irrelevant. (I use SELECT 1 instead). As the term implies, only existence is checked. The expression returns TRUE or FALSE, SELECT items are ignored. It is particularly pointless to add a DISTINCT clause there.

Using CASE in PostgreSQL to affect multiple columns at once

I have a Postgres SELECT statement with these expressions:
,CASE WHEN (rtp.team_id = rtp.sub_team_id)
THEN 'testing'
ELSE TRIM(rtd2.team_name)
END AS testing_testing
,CASE WHEN (rtp.team_id = rtp.sub_team_id)
THEN 'test example'
ELSE TRIM(rtd2.normal_data)
END AS test_response
,CASE WHEN (rtp.team_id = rtp.sub_team_id)
THEN 'test example #2'
ELSE TRIM(rtd2.normal_data_2)
END AS another_example
In my particular query there are 5 fields whose output depends on whether rtp.team_id = rtp.sub_team_id evaluates true. I'm repeating CASE statements with the same condition over and over.
Is there any way I can combine these CASE expressions to toggle the output of multiple columns in one shot?
1. Standard-SQL: LEFT JOIN a single row of values
You could LEFT JOIN a row of values using the condition (thereby evaluating it once). Then you can add fallback values per column with COALESCE().
This syntax variant is shorter and slightly faster with multiple values - especially interesting for an expensive / lengthy condition:
SELECT COALESCE(x.txt1, trim(r2.team_name)) AS testing_testing
, COALESCE(x.txt2, trim(r2.normal_data)) AS test_response
, COALESCE(x.txt3, trim(r2.normal_data_2)) AS another_example
FROM rtp
JOIN rtd2 r2 ON <unknown condition> -- missing context in question
LEFT JOIN (
SELECT 'testing'::text AS txt1
, 'test example'::text AS txt2
, 'test example #2'::text AS txt3
) x ON rtp.team_id = rtp.sub_team_id;
Since the derived table x consists of a single row, joining without further conditions is fine.
Explicit type casts are necessary in the subquery. I use text in the example (which is the default for string literals anyway). Use your actual data types. The syntax shortcut value::type is Postgres-specific, use cast(value AS type) for standard SQL.
If the condition is not TRUE, all values in x are NULL, and COALESCE kicks in.
Or, since all candidate values come from table rtd2 in your particular case, LEFT JOIN to rtd2 using the original CASE condition and CROSS JOIN to a row with default values:
SELECT COALESCE(trim(r2.team_name), x.txt1) AS testing_testing
, COALESCE(trim(r2.normal_data), x.txt2) AS test_response
, COALESCE(trim(r2.normal_data_2), x.txt3) AS another_example
FROM rtp
LEFT JOIN rtd2 r2 ON <unknown condition> -- missing context in question
AND rtp.team_id = rtp.sub_team_id
CROSS JOIN (
SELECT 'testing'::text AS txt1
, 'test example'::text AS txt2
, 'test example #2'::text AS txt3
) x;
It depends on the join conditions and the rest of the query.
2. PostgreSQL-specific
2a. Expand an array
If your various columns share the same data type, you can use an array in a subquery and expand it in the outer SELECT:
SELECT x.combo[1], x.combo[2], x.combo[3]
FROM (
SELECT CASE WHEN rtp.team_id = rtp.sub_team_id
THEN '{test1,test2,test3}'::text[]
ELSE ARRAY[trim(r2.team_name)
, trim(r2.normal_data)
, trim(r2.normal_data_2)]
END AS combo
FROM rtp
JOIN rtd2 r2 ON <unknown condition>
) x;
It gets more complicated if the columns don't share the same data type. You can either cast them all to text (and optionally convert back in the outer SELECT), or you can ...
2b. Decompose a row type
You can use a custom composite type (row type) to hold values of various types and simply *-expand it in the outer SELECT. Say we have three columns: text, integer and date. For repeated use, create a custom composite type:
CREATE TYPE my_type AS (t1 text, t2 int, t3 date);
Or if the type of an existing table matches, you can just use the table name as composite type.
Or if you only need the type temporarily, you can create a TEMPORARY TABLE, which registers a temporary type for the duration of your session:
CREATE TEMP TABLE my_type (t1 text, t2 int, t3 date);
You could even do this for a single transaction:
CREATE TEMP TABLE my_type (t1 text, t2 int, t3 date) ON COMMIT DROP;
Then you can use this query:
SELECT (x.combo).* -- parenthesis required
FROM (
SELECT CASE WHEN rtp.team_id = rtp.sub_team_id
THEN ('test', 3, now()::date)::my_type -- example values
ELSE (r2.team_name
, r2.int_col
, r2.date_col)::my_type
END AS combo
FROM rtp
JOIN rtd2 r2 ON <unknown condition>
) x;
Or even just (same as above, simpler, shorter, maybe less easy to understand):
SELECT (CASE WHEN rtp.team_id = rtp.sub_team_id
THEN ('test', 3, now()::date)::my_type
ELSE (r2.team_name, r2.int_col, r2.date_col)::my_type
END).*
FROM rtp
JOIN rtd2 r2 ON <unknown condition>;
The CASE expression is evaluated once for every column this way. If the evaluation is not trivial, the other variant with a subquery will be faster.
Not sure that it would be an improvement, but you could union the SELECT one way with itself the other way:
SELECT
...,
'testing' AS testing_testing,
'test example' AS test_response,
'test example #2' AS another_example, ...
FROM ...
WHERE rtp.team_id = rtp.sub_team_id AND ...
UNION
SELECT
...,
TRIM(rtd2.team_name) AS testing_testing,
TRIM(rtd2.normal_data) AS test_response,
TRIM(rtd2.normal_data_2) AS another_example, ...
WHERE rtp.team_id <> rtp.sub_team_id AND ...;
The column names can safely be omitted from the second query, assuming you bring them out in the same order as in the first.
You may want to make each of those a separate query using common table expressions (CTEs). If you're worried about this changing the order, you can make it a subquery and apply an ORDER BY around it.