How to combine multiple condition in sql server - sql

Table1
ID
12
21
12
21
...
Conditon
1)
I need to check either id should 12 or id should be 21. It should not be other numbers.
Below query is working
SELECT distinct ltrim(id) from table1 where ltrim(id) = '12' or ltrim(id) = '21')
2)
I dont need muliple number, always 12 or always 21, It should not be mixed, like
id
12
12
12
or
id
21
21
21
Below query is working
Declare #0_Recorddup int = 0
SELECT #0_Recorddup = Count(id) from (SELECT distinct id from table1) t1
if (#0_Recorddup = 0) or (#0_Recorddup > 1)
begin
''error message
end
How to merge a both query, can anyone help me....

ltrim(id) = '12'
You store id's - a numeric value - as a string? I am sorry, but I hope you program better.
For integers it is simple like in most other languages:
id = 12
and yes, etc. aredoable. I would suggest you grab some book about SQL and start learnng basics. Seriously.
I need to check either id should 12 or id should be 21. It should not be other numbers.
Simple. Trivial. Like in any other programming language:
id = 12 OR id = 21
alternative in SQL:
id IN (12, 21)
that is not as nice for 2 numbers but gets in handy fast.
"SQL for Dummies" (IBAM 1118607961, available through amazon etc.) is a decent book for someone at your level. Explains the basics. Like how not to compare numbers as strings.

Related

Oracle SQL Count function

I am hoping someone can advise on the below please?
I have some code (below), it is pulling the data I need with no issues. I have been trying (in vain) to add a COUNT function in here somewhere. The output I am looking for would be a count of how many orders are assigned to each agent. I tried a few diffent things based on other questions but can't seem to get it correct. I think I am placing the COUNT 'Agent' statement and the GROUP BY in the wrong place. Please can someone advise? (I am using Oracle SQL Developer).
select
n.ordernum as "Order",
h.employee as "Name"
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
Output I am looking for would be, for example:
Name Orders Assigned
Bob 3
Peter 6
John 2
Thank you in advance
Name
Total
49
49
49
49
49
John
4
John
4
John
4
John
4
Peter
2
Peter
2
Bob
3
Bob
3
Bob
3
for example. so there are 49 blank rows summed up as 49 in the Total column. I did not add the full 49 blank columns to save space
Would be easier with sample data and expected output, but maybe you are looking for something like this
select
n.ordernum as "Order",
h.employee as "Name",
count(*) over (partition by h.employee) as OrdersAssigned
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
The use of COUNT (as other aggregate functions) is simple.
If you want to add an aggregate function, please group all scalar fields in the GROUP BY clause.
So, in the SELECT you can manage field1, field2, count(1) and so on but you must add in group by (after where conditions) field1, field2
Try this:
select
h.employee as "Name",
count(1) as "total"
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
GROUP BY h.employee

Can I populate the 'in' variable using a list of values stored in one field

I hope I'm not trying to do something that is not possible. ..
In my DB, the query below works and gets the values I want.
select LabelID, Amount
from tCASpreadsData
where LabelID in (3,4,5,7,9,10,11,12,16,17,18,19,21,22,23,24,28,29,30)
However, I don't want to build the list of LabelIDs manually each time. I also don't have a way to logically select them. So, I created a table with all the values listed in one field.
The query below finds the list I want in a field called SumA.
select SumA from tlCECLRatio where CATemplateID = 1 and LabelID = 148
(3,4,5,7,9,10,11,12,16,17,18,19,21,22,23,24,28,29,30)
However, when I combine the two queries, I get nothing.
SELECT LabelID, Amount
FROM tCASpreadsData
WHERE convert(nvarchar(255),LabelID) in
(Select SumA from tlCECLRatio where CATemplateID = 1 and LabelID = 148)
How can I use the value of SumA to create the 'in' list in my where clause?
you can try like below no need conversion
SELECT LabelID, Amount FROM tCASpreadsData where LabelID in
(
Select SumA from tlCECLRatio where CATemplateID = 1 and LabelID = 148
)
This will work:
select LabelID, Amount
from tCASpreadsData
where LabelID in (
select SumA
from tlCECLRatio
where CATemplateID = 1 and LabelID = 148
)
From your OP, it seems like you have the freedom to define the interim table (tlCECLRatio) however you like. So, I would like to suggest that you define it without a varchar field and instead use all integer fields. Here's how it would look with the values you have provided:
CATemplateID LabelID
1 3
1 4
1 5
1 7
1 9
1 10
1 11
1 12
1 16
1 17
1 18
1 19
1 21
1 22
1 23
1 24
1 28
1 29
1 30
If you need other collections of labels, you would give them a new template ID. Each collection is therefore defined by the value of CATemplateID.
To query the values you want, it's a simple join.
select SD.LabelID, SD.Amount
from tCASpreadsData SD inner join tlCECLRatio CR
on SD.LabelID = CR.LabelID
where CR.CATemplateID = 1
Side Note: I have been taught that the interim table also needs its own row ID, so I would probably define it as CECLRatioValue(RatioValueID, CATemplateID, LabelID) where RatioValueID is a sequence (or autonumber) value. But, that might be overkill for a simple cross-reference table. Just pointing out what has been recommended to me as good database practice.

Searching for a number in a database column where column contains series of numbers seperated by a delimeter '"&" in SQLite

My table structure is as follows :
id category
1 1&2&3
2 18&2&1
3 11
4 1&11
5 3&1
6 1
My Question: I need a sql query which generates the result set as follows when the user searched category is 1
id category
1 1&2&3
2 18&2&1
4 1&11
5 3&1
6 1
but i am getting all the results not the expected one
I have tried regexp and like operators but no success.
select * from mytable where category like '%1%'
select * from mytable where category regexp '([.]*)(1)(.*)'
I really dont know about regexp I just found it.
so please help me out.
For matching a list item separated by &, use:
SELECT * FROM mytable WHERE '&'||category||'&' LIKE '%&1&%';
this will match entire item (ie, only 1, not 11, ...), whether it is at list beginning, middle or end.

Is there anything like an Excel IF Statement in SQL

Just wanted to see if there is a way to output a result that is not stored based on the addition of up to 4 fields?
Have a table that holds the count of passengers on a service for different categories, Adult, Child Infant and staff, and I want to try and out put a number based on the result of these fields that is not stored.
For example if the result of the add is >15 then output 45, if > 9 output 35. the output is the size of the coach that is required.
I know I can do it in Excel after the data is extracted but was wondering if it can be done before and included with the data?
Any suggestions and help appreciated.
Depending on what SQL program you're using, most offer the CASE WHEN statement (see this SQL Fiddle example)
CREATE TABLE CaseValues(
Value INT
)
INSERT INTO CaseValues
VALUES (1)
, (16)
, (9)
SELECT CASE WHEN Value > 15 THEN 45
WHEN Value BETWEEN 6 AND 14 THEN 35
ELSE 25 END AS Result
FROM CaseValues
You can also use CASE WHEN on multiple columns, see this example.
The Standard SQL CASE expression is the closest equivalent to the Excel IF(..) function:
SELECT
CASE WHEN Col1=Col2 THEN 'Foo'
WHEN Col1>Col2 THEN 'Bar'
WHEN Col3 Is NULL THEN Col4
ELSE 'unknown'
END As [CaseColumn]
FROM YourTable
You can do this with a query on the data:
select t.*,
(case when Adult + Child + Infant + staff > 15 then 45
when Adult + Child + Infant + staff > 9 then 35
end) as CoachSize
from t;
You can also do this using a view so it is available as if it were a table:
create view vw_t as
select t.*,
(case when Adult + Child + Infant + staff > 15 then 45
when Adult + Child + Infant + staff > 9 then 35
end) as CoachSize
from t;
And, in some databases, you can add a computed column directly into the table definition.
You can use a CASE statement. Here is the syntax.
SELECT
CASE WHEN DayOfBirth > 15
THEN 45
WHEN DayOfBirth > 9
THEN 35
ELSE
0
END
FROM BirthDaysTable

Count the number of rows that contain a letter/number

What I am trying to achieve is straightforward, however it is a little difficult to explain and I don't know if it is actually even possible in postgres. I am at a fairly basic level. SELECT, FROM, WHERE, LEFT JOIN ON, HAVING, e.t.c the basic stuff.
I am trying to count the number of rows that contain a particular letter/number and display that count against the letter/number.
i.e How many rows have entries that contain an "a/A" (Case insensitive)
The table I'm querying is a list of film names. All I want to do is group and count 'a-z' and '0-9' and output the totals. I could run 36 queries sequentially:
SELECT filmname FROM films WHERE filmname ilike '%a%'
SELECT filmname FROM films WHERE filmname ilike '%b%'
SELECT filmname FROM films WHERE filmname ilike '%c%'
And then run pg_num_rows on the result to find the number I require, and so on.
I know how intensive like is and ilike even more so I would prefer to avoid that. Although the data (below) has upper and lower case in the data, I want the result sets to be case insensitive. i.e "The Men Who Stare At Goats" the a/A,t/T and s/S wouldn't count twice for the resultset. I can duplicate the table to a secondary working table with the data all being strtolower and working on that set of data for the query if it makes the query simpler or easier to construct.
An alternative could be something like
SELECT sum(length(regexp_replace(filmname, '[^X|^x]', '', 'g'))) FROM films;
for each letter combination but again 36 queries, 36 datasets, I would prefer if I could get the data in a single query.
Here is a short data set of 14 films from my set (which actually contains 275 rows)
District 9
Surrogates
The Invention Of Lying
Pandorum
UP
The Soloist
Cloudy With A Chance Of Meatballs
The Imaginarium of Doctor Parnassus
Cirque du Freak: The Vampires Assistant
Zombieland
9
The Men Who Stare At Goats
A Christmas Carol
Paranormal Activity
If I manually lay out each letter and number in a column and then register if that letter appears in the film title by giving it an x in that column and then count them up to produce a total I would have something like this below. Each vertical column of x's is a list of the letters in that filmname regardless of how many times that letter appears or its case.
The result for the short set above is:
A x x xxxx xxx 9
B x x 2
C x xxx xx 6
D x x xxxx 6
E xx xxxxx x 8
F x xxx 4
G xx x x 4
H x xxxx xx 7
I x x xxxxx xx 9
J 0
K x 0
L x xx x xx 6
M x xxxx xxx 8
N xx xxxx x x 8
O xxx xxx x xxx 10
P xx xx x 5
Q x 1
R xx x xx xxx 7
S xx xxxx xx 8
T xxx xxxx xxx 10
U x xx xxx 6
V x x x 3
W x x 2
X 0
Y x x x 3
Z x 1
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 x x 1
In the example above, each column is a "filmname" As you can see, column 5 marks only a "u" and a "p" and column 11 marks only a "9". The final column is the tally for each letter.
I want to build a query somehow that gives me the result rows: A 9, B 2, C 6, D 6, E 8 e.t.c taking into account every row entry extracted from my films column. If that letter doesn't appear in any row I would like a zero.
I don't know if this is even possible or whether to do it systematically in php with 36 queries is the only possibility.
In the current dataset there are 275 entries and it grows by around 8.33 a month (100 a year). I predict it will reach around 1000 rows by 2019 by which time I will be no doubt using a completely different system so I don't need to worry about working with a huge dataset to trawl through.
The current longest title is "Percy Jackson & the Olympians: The Lightning Thief" at 50 chars (yes, poor film I know ;-) and the shortest is 1, "9".
I am running version 9.0.0 of Postgres.
Apologies if I've said the same thing multiple times in multiple ways, I am trying to get as much information out so you know what I am trying to achieve.
If you need any clarification or larger datasets to test with please just ask and I'll edit as needs be.
Suggestion are VERY welcome.
Edit 1
Erwin Thanks for the edits/tags/suggestions. Agree with them all.
Fixed the missing "9" typo as suggested by Erwin. Manual transcribe error on my part.
kgrittn, Thanks for the suggestion but I am not able to update the version from 9.0.0. I have asked my provider if they will try to update.
Response
Thanks for the excellent reply Erwin
Apologies for the delay in responding but I have been trying to get your query to work and learning the new keywords to understand the query you created.
I adjusted the query to adapt into my table structure but the result set was not as expected (all zeros) so I copied your lines directly and had the same result.
Whilst the result set in both cases lists all 36 rows with the appropriate letters/numbers however all the rows shows zero as the count (ct).
I have tried to deconstruct the query to see where it may be falling over.
The result of
SELECT DISTINCT id, unnest(string_to_array(lower(film), NULL)) AS letter
FROM films
is "No rows found". Perhaps it ought to when extracted from the wider query, I'm not sure.
When I removed the unnest function the result was 14 rows all with "NULL"
If I adjust the function
COALESCE(y.ct, 0) to COALESCE(y.ct, 4)<br />
then my dataset responds all with 4's for every letter instead of zeros as explained previously.
Having briefly read up on COALESCE the "4" being the substitute value I am guessing that y.ct is NULL and being substituted with this second value (this is to cover rows where the letter in the sequence is not matched, i.e if no films contain a 'q' then the 'q' column will have a zero value rather than NULL?)
The database I tried this on was SQL_ASCII and I wondered if that was somehow a problem but I had the same result on one running version 8.4.0 with UTF-8.
Apologies if I've made an obvious mistake but I am unable to return the dataset I require.
Any thoughts?
Again, thanks for the detailed response and your explanations.
This query should do the job:
Test case:
CREATE TEMP TABLE films (id serial, film text);
INSERT INTO films (film) VALUES
('District 9')
,('Surrogates')
,('The Invention Of Lying')
,('Pandorum')
,('UP')
,('The Soloist')
,('Cloudy With A Chance Of Meatballs')
,('The Imaginarium of Doctor Parnassus')
,('Cirque du Freak: The Vampires Assistant')
,('Zombieland')
,('9')
,('The Men Who Stare At Goats')
,('A Christmas Carol')
,('Paranormal Activity');
Query:
SELECT l.letter, COALESCE(y.ct, 0) AS ct
FROM (
SELECT chr(generate_series(97, 122)) AS letter -- a-z in UTF8!
UNION ALL
SELECT generate_series(0, 9)::text -- 0-9
) l
LEFT JOIN (
SELECT letter, count(id) AS ct
FROM (
SELECT DISTINCT -- count film once per letter
id, unnest(string_to_array(lower(film), NULL)) AS letter
FROM films
) x
GROUP BY 1
) y USING (letter)
ORDER BY 1;
This requires PostgreSQL 9.1! Consider the release notes:
Change string_to_array() so a NULL separator splits the string into
characters (Pavel Stehule)
Previously this returned a null value.
You can use regexp_split_to_table(lower(film), ''), instead of unnest(string_to_array(lower(film), NULL)) (works in versions pre-9.1!), but it is typically a bit slower and performance degrades with long strings.
I use generate_series() to produce the [a-z0-9] as individual rows. And LEFT JOIN to the query, so every letter is represented in the result.
Use DISTINCT to count every film once.
Never worry about 1000 rows. That is peanuts for modern day PostgreSQL on modern day hardware.
A fairly simple solution which only requires a single table scan would be the following.
SELECT
'a', SUM( (title ILIKE '%a%')::integer),
'b', SUM( (title ILIKE '%b%')::integer),
'c', SUM( (title ILIKE '%c%')::integer)
FROM film
I left the other 33 characters as a typing exercise for you :)
BTW 1000 rows is tiny for a postgresql database. It's beginning to get large when the DB is larger then the memory in your server.
edit: had a better idea
SELECT chars.c, COUNT(title)
FROM (VALUES ('a'), ('b'), ('c')) as chars(c)
LEFT JOIN film ON title ILIKE ('%' || chars.c || '%')
GROUP BY chars.c
ORDER BY chars.c
You could also replace the (VALUES ('a'), ('b'), ('c')) as chars(c) part with a reference to a table containing the list of characters you are interested in.
This will give you the result in a single row, with one column for each matching letter and digit.
SELECT
SUM(CASE WHEN POSITION('a' IN filmname) > 0 THEN 1 ELSE 0 END) AS "A",
SUM(CASE WHEN POSITION('b' IN filmname) > 0 THEN 1 ELSE 0 END) AS "B",
SUM(CASE WHEN POSITION('c' IN filmname) > 0 THEN 1 ELSE 0 END) AS "C",
...
SUM(CASE WHEN POSITION('z' IN filmname) > 0 THEN 1 ELSE 0 END) AS "Z",
SUM(CASE WHEN POSITION('0' IN filmname) > 0 THEN 1 ELSE 0 END) AS "0",
SUM(CASE WHEN POSITION('1' IN filmname) > 0 THEN 1 ELSE 0 END) AS "1",
...
SUM(CASE WHEN POSITION('9' IN filmname) > 0 THEN 1 ELSE 0 END) AS "9"
FROM films;
A similar approach like Erwins, but maybe more comfortable in the long run:
Create a table with each character you're interested in:
CREATE TABLE char (name char (1), id serial);
INSERT INTO char (name) VALUES ('a');
INSERT INTO char (name) VALUES ('b');
INSERT INTO char (name) VALUES ('c');
Then grouping over it's values is easy:
SELECT char.name, COUNT(*)
FROM char, film
WHERE film.name ILIKE '%' || char.name || '%'
GROUP BY char.name
ORDER BY char.name;
Don't worry about ILIKE.
I'm not 100% happy about using the keyword 'char' as table title, but hadn't had bad experiences so far. On the other hand it is the natural name. Maybe if you translate it to another language - like 'zeichen' in German, you avoid ambiguities.