I am trying to select a default value to a column if the column does not exist in the table. Following code seems doesn't give my expected output.
SELECT CustomerID, (select case when exists (select *
from INFORMATION_SCHEMA.COLUMNS
where table_name = 'Customers' and
column_name = 'Age'
)
then Age
else 0 as Age
end)
FROM (select * from Customers);
Since Age column doesn't exist in the table result should be given as:-
CustomerID | Age
-----------|----
Cust01 | 0
Cust02 | 0
Can someone suggest me a solution or the error in above code snippet.
As mentioned in the comments by experts, you need to use a Oracle PLSQL block and achieve your requirement using dynamic sql. Please see below the same code which is written keeping your requirement in mind. You can try implementing your code with this:
declare
var number;
var2 varchar2(4000);
type abc is record
( col1 number,
col2 number);
type var3 is table of abc index by pls_integer;
var4 var3;
begin
select count(1)
into var
from employee;
var2:= 'select A, '||case when var is null then 1 else 2 end || ' from test where rownum < 10';
dbms_output.put_line(var2);
execute immediate var2 bulk collect into var4;
for rec in 1..var4.last
loop
dbms_output.put_line(var4(rec).col1 ||',' ||var4(rec).col2);
end loop;
end;
Output:
select A, 2 from test where rownum < 10
1444,2
1445,2
1446,2
1447,2
1448,2
1449,2
1450,2
1451,2
1452,2
Related
I have 31 days in a table as d1 to d31.
I'm getting id from another table and using it in a WHERE clause
and I need to check in d1-d31 which columns have x or y char present in SQL Server.
This is a function I'm trying.
How can I optimise this from IN to IF and not using dXX 31 times?
declare #delete as bit
set #delete=0
if exists(select * from tblname where id=#id and(d1 in('x','y') or D2 in('x','y')....,D31 in('x','y'))
return #delete
else
set #delete =1
return #delete
SELECT
CASE
WHEN id = #id AND D1IN ('x','y')
THEN 0
ELSE 1
END AS Thing
FROM (
SELECT D1 AS d FROM Somewhere
UNION
SELECT D2 FROM Somewhere
...
UNION
SELECT D31 FROM Somewhere
) T
It is the nature of SQL that you have to write out all of the column names.
If this is a problem then it's probably a problem with your schema being unsuited for the queries you want to write against it.
You could bodge it up by doing something like
CREATE VIEW D
AS
SELECT 1 AS D, D1 AS Value FROM Somewhere
UNION
...
UNION
SELECT 31 AS D, D31 As Value FROM Somewhere
I have a column called empl_type_multi which is just a comma delimited column, each value is a link to another table called custom captions.
For instance, i might have the following as a value in empl_type_multi:
123, RHN, 458
Then in the custom_captions table these would be individual values:
123 = Dog
RHN = Cat
458 = Rabbit
All of these fields are NTEXT.
What i am trying to do is convert the empl_type_multi column and chance it to the respective names in the custom_captions table, so in the example above:
123, RHN, 458
Would become
Dog, Cat, Rabbit
Any help on this would be much appreciated.
----- EDIT ------------------------------------------------------------------
Ok so ive managed to convert the values to the corresponding caption and put it all into a temporary table, the following is the output from a CTE query on the table:
ID1 ID2 fName lName Caption_name Row_Number
10007 22841 fname1 lname1 DENTAL ASSISTANT 1
10007 22841 fname1 lname1 2
10007 22841 fname1 lname1 3
10008 23079 fname2 lname2 OPS WARD 1
10008 23079 fname2 lname2 DENTAL 2
10008 23079 fname2 lname2 3
How can i update this so that anything under caption name is added to the caption name of Row_Number 1 separated by a comma?
If i can do that all i need to do is delete all records where Row_Number != 1.
------ EDIT --------------------------------------------------
The solution to the first edit was:
WITH CTE AS
(
SELECT
p.ID1
, p.ID2
, p.fname
, p.lname
, p.caption_name--
, ROW_NUMBER() OVER (PARTITION BY p.id1ORDER BY caption_name DESC) AS RN
FROM tmp_cs p
)
UPDATE tblPerson SET empType = empType + ', ' + c.Data
FROM CTE c WHERE [DB1].dbo.tblPerson.personID = c.personID AND RN = 2
And then i just incremented RN = 2 until i got 0 rows affected.
This was after i ran:
DELETE FROM CTE WHERE RN != 1 AND Caption_name = ''
select ID1, ID2, fname, lname, left(captions, len(captions) - 1) as captions
from (
select distinct ID1, ID2, cast(fname as nvarchar) as fname, cast(lname as nvarchar) as lname, (
select cast(t1.caption_name as nvarchar) + ','
from #temp as t1
where t1.ID1 = t2.ID1
and t1.ID2 = t2.ID2
and cast(caption_name as nvarchar) != ''
order by t1.[row_number]
for xml path ('')) captions
from #temp as t2
) yay_concatenated_rows
This will give you what you want. You'll see casting from ntext to varchar. This is necessary for comparison because many logical ops can't be performed on ntext. It can be implicitly cast back the other way so no worries there. Note that when casting I did not specify length; this will default to 30, so adjust as varchar(length) as needed to avoid truncation. I also assumed that both ID1 and ID2 form a composite key (it appears this is so). Adjust the join as you need for the relationship.
you have just shared your part of problem,not exact problem.
try this,
DECLARE #T TABLE(ID1 VARCHAR(50),ID2 VARCHAR(50),fName VARCHAR(50),LName VARCHAR(50),Caption_name VARCHAR(50),Row_Number INT)
INSERT INTO #T VALUES
(10007,22841,'fname1','lname1','DENTAL ASSISTANT', 1)
,(10007,22841,'fname1','lname1', NULL, 2)
,(10007,22841,'fname1','lname1', NULL, 3)
,(10008,23079,'fname2','lname2','OPS WARD', 1)
,(10008,23079,'fname2','lname2','DENTAL', 2)
,(10008,23079,'fname2','lname2', NULL, 3)
SELECT *
,STUFF((SELECT ','+Caption_name
FROM #T T1 WHERE T.ID1=T1.ID1 FOR XML PATH('')
),1,1,'')
FROM #T T
You can construct the caption_name string easily by looping through while loop
declare #i int = 2,#Caption_name varchar(100)= (select series from
#temp where Row_Number= 1)
while #i <= (select count(*) from #temp)
begin
select #Caption_name = #Caption_name + Caption_name from #temp where Row_Number = #i)
set #i = #i+1
end
update #temp set Caption_name = #Caption_name where Row_Number = 1
and use case statement to remove null values
(select case when isnull(Caption_name ,'') = '' then
'' else ',' + Caption_name end
While using case when in where clause in sql query it's not working.
Problem :
I have two tables named TblEmployee and TblAssociate.Both tables contains common columns PeriodId, EmpId and AssociateId. My requirement is to fetch values from
TblEmployee with combination of EmpId and AssociateId from TblAssociate should be excluded.And the exclusion should be based on PeriodId condition.`
If(#PeriodID<50)
BEGIN
SELECT *
FROM TblEmployee
WHERE (EmpId+AssociateId) NOT IN (SELECT EmpId+AssociateId FROM TblAssociate)
END
ELSE
BEGIN
SELECT *
FROM TblEmployee
WHERE (EmpId) NOT IN (SELECT EmpId FROM TblAssociate)
END
The above code is working, but I need to avoid that IF-ELSE condition and I wish to use 'case when' in where clause.Please help
Try this:
SELECT *
FROM TblEmployee
WHERE (EmpId + CASE WHEN #PeriodID<50 THEN AssociateId ELSE 0 END) NOT IN
(SELECT EmpId + CASE WHEN #PeriodID<50 THEN AssociateId ELSE 0 END FROM TblAssociate)
You say your code is working but this is rather odd, since it doesn't make much sense to add together id values. In any case, the above statement produces a result that is equivalent to the code originally posted.
You could use AND-OR combination in the WHERE clause. Additionally, you should not be using + as it may lead to incorrect result. You can rewrite your query as:
SELECT e.*
FROM TblEmployee e
WHERE
(
#PeriodID < 50
AND NOT EXISTS(
SELECT 1
FROM TblAssociate a
WHERE
a.EmpId = e.EmpId
AND a.AssociateId = e.AssociateId
)
)
OR
(
#PeriodID >= 50
AND NOT EXISTS(
SELECT 1
FROM TblAssociate a
WHERE a.EmpId = e.EmpId
)
)
The addition of IDs do not guarantee uniqueness. For instance, if EmpId is 5 and AssociateId is 6, then EmpId + AssociateId = 11, while EmpId + AssociateId = 11 even if EmpId is 6 and AssociateId is 5. In the query below, I made sure that the subquery will stop searching when the first record is found and will return a single record, having the value of 1. We select the employee if and only if 1 is among the results. In the subquery we check the operand we are sure of first and then check if we are not in a period where AssociateId must be checked, or it matches.
select *
from TblEmployee
where 1 in (select top 1 1
from TblAssociate
where TblEmployee.EmpId = TblAssociate.EmpId and
(#PeriodID >= 50 or TblEmployee.AssociateId = TblAssociate.AssociateId))
Technologies: SQL Server 2008
So I've tried a few options that I've found on SO, but nothing really provided me with a definitive answer.
I have a table with two columns, (Transaction ID, GroupID) where neither has unique values. For example:
TransID | GroupID
-----------------
23 | 4001
99 | 4001
63 | 4001
123 | 4001
77 | 2113
2645 | 2113
123 | 2113
99 | 2113
Originally, the groupID was just chosen at random by the user, but now we're automating it. Thing is, we're keeping the existing DB without any changes to the existing data(too much work, for too little gain)
Is there a way to query "GroupID" on table "GroupTransactions" for the next available value of GroupID > 2000?
I think from the question you're after the next available, although that may not be the same as max+1 right? - In that case:
Start with a list of integers, and look for those that aren't there in the groupid column, for example:
;WITH CTE_Numbers AS (
SELECT n = 2001
UNION ALL
SELECT n + 1 FROM CTE_Numbers WHERE n < 4000
)
SELECT top 1 n
FROM CTE_Numbers num
WHERE NOT EXISTS (SELECT 1 FROM MyTable tab WHERE num.n = tab.groupid)
ORDER BY n
Note: you need to tweak the 2001/4000 values int the CTE to allow for the range you want. I assumed the name of your table to by MyTable
select max(groupid) + 1 from GroupTransactions
The following will find the next gap above 2000:
SELECT MIN(t.GroupID)+1 AS NextID
FROM GroupTransactions t (updlock)
WHERE NOT EXISTS
(SELECT NULL FROM GroupTransactions n WHERE n.GroupID=t.GroupID+1 AND n.GroupID>2000)
AND t.GroupID>2000
There are always many ways to do everything. I resolved this problem by doing like this:
declare #i int = null
declare #t table (i int)
insert into #t values (1)
insert into #t values (2)
--insert into #t values (3)
--insert into #t values (4)
insert into #t values (5)
--insert into #t values (6)
--get the first missing number
select #i = min(RowNumber)
from (
select ROW_NUMBER() OVER(ORDER BY i) AS RowNumber, i
from (
--select distinct in case a number is in there multiple times
select distinct i
from #t
--start after 0 in case there are negative or 0 number
where i > 0
) as a
) as b
where RowNumber <> i
--if there are no missing numbers or no records, get the max record
if #i is null
begin
select #i = isnull(max(i),0) + 1 from #t
end
select #i
In my situation I have a system to generate message numbers or a file/case/reservation number sequentially from 1 every year. But in some situations a number does not get use (user was testing/practicing or whatever reason) and the number was deleted.
You can use a where clause to filter by year if all entries are in the same table, and make it dynamic (my example is hardcoded). if you archive your yearly data then not needed. The sub-query part for mID and mID2 must be identical.
The "union 0 as seq " for mID is there in case your table is empty; this is the base seed number. It can be anything ex: 3000000 or {prefix}0000. The field is an integer. If you omit " Union 0 as seq " it will not work on an empty table or when you have a table missing ID 1 it will given you the next ID ( if the first number is 4 the value returned will be 5).
This query is very quick - hint: the field must be indexed; it was tested on a table of 100,000+ rows. I found that using a domain aggregate get slower as the table increases in size.
If you remove the "top 1" you will get a list of 'next numbers' but not all the missing numbers in a sequence; ie if you have 1 2 4 7 the result will be 3 5 8.
set #newID = select top 1 mID.seq + 1 as seq from
(select a.[msg_number] as seq from [tblMSG] a --where a.[msg_date] between '2023-01-01' and '2023-12-31'
union select 0 as seq ) as mID
left outer join
(Select b.[msg_number] as seq from [tblMSG] b --where b.[msg_date] between '2023-01-01' and '2023-12-31'
) as mID2 on mID.seq + 1 = mID2.seq where mID2.seq is null order by mID.seq
-- Next: a statement to insert a row with #newID immediately in tblMSG (in a transaction block).
-- Then the row can be updated by your app.
I have a table like the following which is basically used to "give a name" to a value in a table (this table contains values for a bunch of other tables as well, not just for MYTABLE; I've omitted a few irrelevant fields from NAMEVALUEMAP):
NAMEVALUEMAP Table
---------------------
VALUE_ | NAME_
---------------------
0 | ZERO
1 | ONE
I didn't want to use JOINs so I thought of using Sub-Queries.
Problem is when a value does not exist in the NAMEVALUEMAP table then NULL is shown.
Instead of NULL I want to show the actual value from MYTABLE (MYTABLE has ID field as identity column and contains a few rows):
-- //Fine, prints word 'ZERO' when MYTABLE.ABC is 0
SELECT
(SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID))
FROM
MYTABLE outer_
-- //Not Fine, prints NULL (because "999" is not in NAMEVALUEMAP). In this case, MYTABLE.ABC is 999
-- //Want it to print 999 if the value is not in NAMEVALUEMAP
SELECT
(SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID))
FROM
MYTABLE outer_
-- //Tried COALESCE, but the error is "Invalid column name 'VALUE_'"
SELECT
COALESCE((SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID)), ABC)
FROM
MYTABLE outer_
Also, is there a better way to do this sort of value-to-name mapping?
I would recomend using a LEFT JOIN (is there any reason you are voidung it?) and ISNULL
SELECT ISNULL(NAME_, ABC)
FROM MYTABLE m LEFT JOIN
NAMEVALUEMAP n ON m.ABC = n.VALUE_
Well, in that case you can try
SELECT ISNULL((select NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = m.ABC), m.ABC)
FROM MYTABLE m
It is a left join, unless you want soem EXISTS/UNION construct. Not tested:
SELECT
COALESCE(N.VALUE, M.ABC)
FROM
MYTABLE M
LEFT JOIN
NAMEVALUEMAP N ON M.VALUE N.ABC
If you really want to avoid JOINs...
SELECT
ABC
FROM
MYTABLE M
WHERE
NOT EXISTS (SELECT * FROM NAMEVALUEMAP N WHERE M.VALUE N.ABC)
UNION ALL
SELECT
VALUE
FROM
NAMEVALUEMAP N
WHERE
EXISTS (SELECT * FROM MYTABLE M WHERE M.VALUE N.ABC)
Edit:
The SELECT *, 1 or NULL in EXISTS question again
Try EXISTS (SELECT 1/0...)
Mentioned in ANSI SQL 1992 Standard too, page 191
EDIT:
SELECT
COALESCE(
(SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ =
(SELECT ABC FROM MYINNERTABLE inner_ WHERE inner_.ID = outer_.ID)
),
<int to string>(
SELECT ABC FROM MYINNERTABLE inner_ WHERE inner_.ID = outer_.ID
)
)
FROM
MYTABLE outer_
where column function <int to string> is appropriate for sqlserver. In mysql it would be CAST(). Without conversion, the query will throw a wobbly about the mismatched datatypes.