Using IN clause in sql server - sql-server-2008

My query is like below.I want to select values if Type = 1 and subtype = 1,3 or 2.
select sum(case when Type = 1 and SubType in (1, 3 or 2) then 1 else 0 end) as 'WorkStations'
Is this right way?

Since you're only trying to get a count of the workstations that meet the criteria as far as I can see:
SELECT COUNT(*) AS Workstations FROM MyWorkStationTable WHERE Type = 1 AND SubType IN (1, 2, 3)
Also, an IN clause is by nature already an OR. It is neither valid syntax nor necessary to state it.

If you're simply counting records, your best bet is to use the COUNT function provided by SQL Server. Consider using the following:
SELECT COUNT(*) FROM [Table] WHERE TYPE = 1
AND (SUBTYPE = 1
OR SUBTYPE = 2
OR SUBTYPE = 3)
It is best to avoid using 'IN' as it can lead to unnecessary calls to the SQL engine.

SELECT COUNT(*) [Workstations] FROM [YourTable] t WHERE t.Type = 1 AND t.SubType IN (1, 2, 3)

Try avoiding IN Predicates and instead use Joins because it Iterate unnecessarily despite of the fact that there is just one/two match. I will explain it with an example.
Suppose I have two list objects.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Using IN, it will search for each List-1 item in List-2 that means iteration will happen 49 times !!!

Related

Recursively running a MySQL function

I have a function in MySQL that needs to be run about 50 times (not a set value) in a query. the inputs are currently stored in an array such as
[1,2,3,4,5,6,7,8,9,10]
when executing the MySQL query individually it's working fine, please see below
column_name denotes the column it's getting the data for, in this case, it's a DOUBLE in the database
The second value in the MOD() function is the input I'm supplying MySQL from the aforementioned array
SELECT id, MOD(column_name, 4) AS mod_output
FROM table
HAVING mod_output > 10
To achieve the output I require* the following code works
SELECT id, MOD(column_name, 4) AS mod_output1, MOD(column_name, 5) AS mod_output2, MOD(column_name, 6) AS mod_output3
FROM table
HAVING mod_output1 > 10 AND mod_output2 > 10 AND mod_output3 > 10
However this obviously is extremely dirty, and when having not 3 inputs, but over 50, this will become highly inefficient.
Appart from calling over 50 individual querys, is there a better way to acchieve the same sort (see below) of output?
In escennce i need to supply MySQL with a list of values and have it run MOD() over all of them on a specified column.
The only data I need returned is the id's of the rows that match the MOD() functions output with the specified input (see value 2 of the MOD() function) where the output is less than 10
Please note, MOD() has been used as an example function, however, the final function required *should* be a drop in replacement
example table layout
id | column_name
1 | 0.234977
2 | 0.957739
3 | 2.499387
4 | 48.395777
5 | 9.943782
6 | -39.234894
7 | 23.49859
.....
(The title may be worded wrong, I'm not quite sure how else you'd explain what I'm trying to do here)
Use a join and derived table or temporary table:
SELECT n.n, t.id, MOD(t.column_name, n.n) AS mod_output
FROM table t CROSS JOIN
(SELECT 4 as n UNION ALL SELECT 5 UNION ALL SELECT 6 . . .
) n
WHERE MOD(t.column_name, n.n) > 10;
If you want the results as columns, you can use conditional aggregation afterwards.

SQL: How to check "if this record exists then that record must also exist" for given ID set

my database table (DWInfo) looks like this:
InstanceID | AttributeID
1 | 1
1 | 2
1 | 3
2 | 1
2 | 4
3 | 1
3 | 2
There are several instances and every instance has multiple attributes.
What I want to achieve is this: for a given set/rule of id's I want to get all InstanceID's which violate the condition, for example let the given ID's be 1 and 2, which means if there is an instance with AttributeID=1, Attribute=2 should also exist for it. In this case the result would be instance two, because this instance violates the condition.
I tried it with JOINS but this only seemed effective for 2 attributes and not more.
Select * from DWInfo dw1 INNER JOIN DWInfo dw2 ON dw1.InstanceID = dw2.InstanceID where dw1.AttributeID != dw2.AttributeID and dw1.AttributeID = 1 AND dw2.AttributeID != 2
Is it possible to solve this problem with a SQL query?
Assuming that each InstanceId can have only one of each different AttributeId, i.e. a unique composite index (InstanceId, AttributeId):
SELECT InstanceID
FROM DWInfo
WHERE AttributeID IN (1,2)
GROUP BY InstanceID
HAVING SUM(AttributeId = 1) = 1
AND COUNT(*) < 2 /* Or SUM(AttributeId = 2) = 0 */
SQLFiddle DEMO
Note that if having AttributeId of 2 means that the instance requires an AttributeId of 1 also.. slightly different logic, this is neater:
SELECT InstanceID
FROM DWInfo
WHERE AttributeID IN (1,2)
GROUP BY InstanceID
HAVING COUNT(*) < 2
Where there exists Attribute 1 find the ones that don't have Attribute 2.
select InstanceID
from DWInfo
group by InstanceID
having
count(case when AttributeID = 1 then 1 end) > 0
and count(case when AttributeID = 2 then 1 end) = 0
This answer is basically the same as Arth's. You might find it beneficial to filter the Attributes in the where clause but it's not strictly necessary. I prefer the standard syntax using case expressions even though the shorthand would be handy if it were portable. I also prefer count over sum in these scenarios.
It's not clear whether you can have duplicates (probably not) and whether Attribute 2 can appear alone. You might have to tweak the numbers a bit but you should be able to follow the pattern.
I think this does what you want:
select instanceid
from dwinfo
where attributeid in (1, 2)
group by instanceid
having count(*) = 2;
This guarantees that you have two matching rows for each instance. If you can have duplicates, then use:
having count(distinct attributeid) = 2
EDIT:
For the conditional version (if 1 --> 2):
having max(attributeid = 2) > 0
That is, if it has 1 or 2, then it has to have 2, and everything is ok.

searching for records in mysql using or - and - not in query

I think I am getting turned around when looking at this. I am trying to get all patron records relating to transactions that have a transaction item with one of a number of ids (1 or 2) as well as transaction items with other ids (3 or 4) but not with transaction items with other ids (5 or 6)
The structure is:
=patron=
id
fname
lname
email
phone
=trans=
id
id_org
id_patron
=trans_item=
id
id_trans
id_perf
I was trying the following:
SELECT
patron.email,
patron.fname,
patron.lname,
patron.phone
FROM
trans_item,
trans,
patron
WHERE
trans_item.id_perf IN (1,2)
AND
trans_item.id_perf IN (3,4)
AND
trans_item.id_perf NOT IN (5,6)
AND
trans_item.id_trans = trans.id
AND
trans.id_org = 1
AND
trans.id_patron = patron.id
GROUP BY
patron.id
ORDER BY
patron.email DESC,
patron.phone DESC
I'm aware that saying the id needs to be 2 AND 4 is always going to return nothing but I need to have it as if id is in (1,2) AND (3,4) so it can be 1 or 2 but also needs to be in 3 or 4
For Clarity:
I am trying to get patrons who have gone to performance 1 OR 2 and 3 OR 4 but NOT 5 OR 6
You can do this with group by and having. The basic idea is:
select ti.id_trans
from trans_item ti
group by ti.id_trans
having sum(ti.id_perf in (1, 2)) > 0 and
sum(ti.id_perf in (3, 4)) > 0 and
sum(ti.id_perf in (5, 6)) = 0;
Each condition in the having clause checks a row for the particular ids. The > 0 means they exist for transaction. The = 0 means they do not.
If you want additional columns from other tables, you can join back to this result set.
I think I have a solution. If I combine the ids for all perfs and group all results by the trans_item.id I can get a list that has duplicates. I then convert them into a php multidimensional array and exclude / include based on the ids for each requirement finding the duplicates that way. Any other suggestions are welcome

Using concat in where conditions, good or bad?

A simple quiz:
Probably many guys know this before,
In my app there is a query in which Im using concat in where condition like this,
v_book_id and v_genre_id are 2 variables in my procedure.
SELECT link_id
FROM link
WHERE concat(book_id,genre_id) = concat(v_book_id,v_genre_id);
Now, I know there is a catch/bug in this, which will occur only twice in your lifetime. Can you tell me what is it?
I found this out yesterday and thought I should make a noise about all others practicing this.
Thanks.
Let's have a look
WHERE concat(book_id,genre_id) = concat(v_book_id,v_genre_id);
as opposed to
WHERE book_id = v_book_id AND genre_id = v_genre_id;
There. The second solution is
faster (optimal index usage)
easier to write (less code)
easier to read (what on earth was the author thinking to concatenate numbers???)
more correct (as Alnitak also stated in the question's comments). check out this sample data:
book_id | genre_id
1 | 12
11 | 2
Now add (or concat) v_book_id = 1 and v_genre_id = 12 and see how you'll get funny results with your concat() query
Note, some databases (including MySQL) allow operations on tuples, which may be what the clever author of the above really intended to do:
WHERE (book_id, genre_id) = (v_book_id, v_genre_id);
A working example of such a tuple predicate:
SELECT * FROM (
SELECT 1 x, 2 y FROM DUAL UNION ALL
SELECT 1 x, 3 y FROM DUAL UNION ALL
SELECT 1 x, 2 y FROM DUAL
) a
WHERE (x, y) = (1, 2)
Note, some databases will need extra parentheses around the right-hand side tuple : ((1, 2))

How to transform rows into columns having no column with unique values?

I am trying to transform some rows into columns in MySQL. I know it has been asked and answered previously, like here.
My problem is, there is nothing in my rows on which I can apply the 'if' construct. (At least I think so.)
E.g. For the following input,
2 5 1000
2 6 2000
I can run this query:
INSERT INTO SUMMARY
(user_id,valueA,valueB)
SELECT d.user_id,
MAX(CASE WHEN d.code = 5 THEN d.value ELSE NULL END),
MAX(CASE WHEN d.code = 6 THEN d.value ELSE NULL END),
FROM DETAILS d
GROUP BY d.user_id
and get this output:
2 1000 2000
But my problem is, my input is something like this:
2 6 1000
2 6 2000
(The values in the second column are not unique.)
And i still need the same output, i.e.:
2 1000 2000
Can it be done in MySQL? If yes, can anyone help me with this?
Well, if you don't have any idea of how many columns there will be in your pivot table, nor have a value to decide which value should go in a given column, the best solution I can recommend is to use a GROUP_CONCAT function and then do some parsing in your code:
SELECT d.user_id, GROUP_CONCAT(
d.value SEPARATOR ','
) AS val
FROM details d
GROUP BY d.user_id;