How to write a multple Not equal AND Clause in TSQL? - sql

I am using tsql to write query and my where clause seems to be returning too many results. It for some reason seems to be treating my Not equal AND Clause as an OR clause.
SELECT *
FROM [dbo].TestTable
WHERE
([SomeID] <> 0)
AND
([BlahID] <> 0)
AND
([TestID] <> 0)
AND
([AnotherID] <> 0)
ORDER By ID
I need it to return only rows where all four columns are not equal to zero. Right now it is removing rows where any of the columns are equal to zero.
My Table:
ID SomeID BlahID TestID AnotherID
1 0 1 4 0
2 0 4 4 8
3 0 0 4 0
4 0 1 4 0
5 6 1 4 3
What I want it to return
ID SomeID BlahID TestID AnotherID
1 0 1 4 0
2 0 4 4 8
4 0 1 4 0
5 6 1 4 3
What it Returns
ID SomeID BlahID TestID AnotherID
5 6 1 4 3

It keeps not returning any row that has a zero in any of these columns
not all of these columns
That's exactly what your query is all about. It returns rows, with no 0 at any column you've listed.
That's because if any of AND parts is evaluated as false entire condition is false as well. So if e.g. TestID = 0, the entire WHERE clause is false and the row is not being returned.
To get rows where at lease one of these columns has non-zero value, use OR.

I need it to return only rows where all four columns are not equal to
zero. Right now it is removing rows where any of the columns are equal
to zero.
Based on your wording and revised update, what you do want is to use OR instead of AND. Not sure if the link will work, but you can check out this SQL Fiddle.

Easier to read (for me, at least)
Records that never have and id = 0
SELECT *
FROM [dbo].TestTable
WHERE 0 NOT IN ([SomeID],[BlahID],[TestID],[AnotherID])
Records that have at least one id <> 0
SELECT * FROM TestTable
WHERE EXISTS (
SELECT 1
FROM (VALUES(SomeID),(BlahID),(TestID),(AnotherID)) t(id)
WHERE id <> 0
)

Use
SELECT * FROM [dbo].TestTable WHERE [BlahID] <> 0
The problem isn't your query, it's the results you expect. Every row in the table has a non-zero value in at least one column, so the query returns the whole table.

Related

How to update table by of the records in the table

I have a table in PostgerSQL and I need to make N entries in the table twice and for the first half I need to fill in the partner_id field with the value 1 and the second half with the value partner_id = 2.
i try to `
update USERS_TABLE set user_rule_id = 1;
update USERS_TABLE set user_rule_id = 2 where USERS_TABLE.id > count(*)/2;
`
I depends a lot how precise the number of users have to be that are updated with 1 or 2.
The following would be quite unprecise,a s it doesn't take the exact number of user that already exist8after deleting some rows the numbers doesn't fit anymore.
SELECT * FROM USERS_TABLE
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
If you have a lot of deleted rows and want still the half of the users, you can choose following approach, which does rely on the id, but at teh actual row number
UPDATE USERS_TABLE1
set user_rule_id = CASE WHEN rn <= (SELECT count(*) FROM USERS_TABLE1)/ 2 then 1
ELSE 2 END
FROM (SELECT id, ROW_NUMBER() OVER( ORDER BY id) rn FROM USERS_TABLE1) t
WHERE USERS_TABLE1.id = t.id;
UPDATE 5
SELECT * FROM USERS_TABLE1
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
fiddle
In the sample case it it the same result, but when you have a lot of rows and a bunch of the deleted users, the senind will give you quite a good result

Query for two different scenarios

I need to write a query which acheives the following requirements
1.) Need to get one row for the combination of "PrimaryID1" and "PrimaryID2", there are more than one field for this combination.
2.) There are two sub conditions here we need to take in to account.
2.a) For fields with "FlagField = 1"
In some combination of "PrimaryID1" and "PrimaryID2" we have "FlagField = 1" in this case only that particular field needs to be taken
2.b) For fields with "FlagField IS NULL"
If "FlagField IS NULL" then the requirement is like we need to pull the record for min of "PrimaryID3"(its a diff. field)
Here is how the data looks like
-----------------------------------------------------------------------------------------------
PrimaryID1 PrimaryID2 PrimaryID3 FlagField DataField1 DataField2
-----------------------------------------------------------------------------------------------
PPID11 PPID21 1 0 DF111 DF211
PPID11 PPID21 2 1 DF112 DF212
PPID11 PPID21 3 0 DF113 DF213
PPID12 PPID22 1 NULL DF121 DF221
PPID12 PPID22 2 NULL DF122 DF222
PPID12 PPID22 3 NULL DF123 DF223
Sample result
-----------------------------------------------------------------------------------------------
PrimaryID1 PrimaryID2 PrimaryID3 FlagField DataField1 DataField2
-----------------------------------------------------------------------------------------------
PPID11 PPID21 2 1 DF112 DF212
PPID12 PPID22 1 NULL DF121 DF221
You can use ROW_NUMBER to identify the row with the lowest PrimaryID3. Wrapping that in a case statement will allow you to 'conditionally combine' it with the FlagField. You then just need to filter on the result of the expression.
WITH cte AS
(
SELECT *, r = CASE WHEN FlagField IS NOT NULL THEN FlagField ELSE ROW_NUMBER() OVER(PARTITION BY PrimaryID1, PrimaryID2 ORDER BY CAST(REPLACE(PrimaryID3, 'PPID', '') AS INT)) END
FROM tbl
)
SELECT *
FROM cte
WHERE r = 1

SQL - how to return rows containing ALL children rows

Given a parent + reference tables where Reference table is as follows
Ref_ID PARENT_ID
-------------------
1 1
2 1
1 2
3 2
1 3
3 3
4 3
2 4
3 4
I'm trying to return all distinct parent rows where ref_id contains both 2 & 3
The query
SELECT *
FROM Parent
WHERE parent_id in (SELECT parent_id from XRefTable where ref_id in (2, 3) )
returns all parent_id 1, 2, 3, 4
WHEREAS the correct result required is to return parent_id 4 which has BOTH ref_id's 2 & 3, others have EITHER 2 OR 3
Any help is appreciated
FYI - there are 4-7 tables in the query (depending on user selections) so performance is a huge factor
SORRY cannot use stored procedures as it has to work on SQL Server CE too
SELECT parent_id
from XRefTable
where ref_id in ( 2, 3 )
group by PARENT_ID
having count(distinct ref_id) = 2
You could do this:
SELECT
ParentReference.Parent_ID
FROM
ParentReference
INNER JOIN ParentReference B ON ParentReference.Parent_ID = B.Parent_ID AND ParentReference.Ref_ID = 2 AND B.Ref_ID = 3
You are trying to do a set-wise comparison. For this, I strongly recommend the group by and having clauses:
select parent_id
from Reference r
group by parent_id
having sum(case when ref_id = 2 then 1 else 0 end) > 0 and
sum(case when ref_id = 3 then 1 else 0 end) > 0
Each component of the having clause is counting one of the fields. The logic requires that both are present.
The reason that I prefer this approach over others is because you can change the logic, using essentially the same structure.
If you have the list in a comma delimited string, the following will work. Perhaps not "elegant" and "relational", but it works:
set #Ref_ids = "1,2,3,4"
select parent_id
from Reference r
where charindex(','+cast(ref_id as varchar(255))+',', '+#ref_ids+',') > 0
group by parent_id
having count(distinct ref_id) = (len(replace(#ref_ids, ',', '')) - len(#ref_ids))+1
This is doing string manipulation to determine whether the ref_id is in the list. The having clause then counts the number of matches, making sure that it is the same size as the list. This will work, assuming there are no spaces in the list and no blank values.

Returning several rows from a single query, based on a value of a column

Let's say I have this table:
|Fld | Number|
1 5
2 2
And I want to make a select that retrieves as many Fld as the Number field has:
|Fld |
1
1
1
1
1
2
2
How can I achieve this? I was thinking about making a temporary table and instert data based on the Number, but I was wondering if this could be done with a single Select statement.
PS: I'm new to SQL
You can join with a numbers table:
SELECT Fld
FROM yourtable
JOIN Numbers
ON yourtable.Number <= Numbers.Number
A numbers table is just a table with a list of numbers:
Number
1
2
3
etc...
Not an great solution (since you still query your table twice, but maybe you can work from it)
SELECT t1.fld, t1.number
FROM table t1, (
SELECT ROWNUM number FROM dual
CONNECT BY LEVEL <= (SELECT MAX(number) FROM t1)) t2
WHERE t2.number<=t1.number
It generates maximum amount of rows needed and then filters it by each row.
I don't know if your RDBMS version supports it (although I rather suspect it does), but here is a recursive version:
WITH remaining (fld, times) as (SELECT fld, 1
FROM <table>
UNION ALL
SELECT a.fld, a.times + 1
FROM remaining as a
JOIN <table> as b
ON b.fld = a.fld
AND b.number > a.times)
SELECT fld
FROM remaining
ORDER BY fld
Given your source data table, it outputs this (count included for verification):
fld times
=============
1 1
1 2
1 3
1 4
1 5
2 1
2 2

MySQL - How to simplify this query?

i have a query which i want to simplify:
select
sequence,
1 added
from scoredtable
where score_timestamp=1292239056000
and sequence
not in (select sequence from scoredtable where score_timestamp=1292238452000)
union
select
sequence,
0 added
from scoredtable
where score_timestamp=1292238452000
and sequence
not in (select sequence from scoredtable where score_timestamp=1292239056000);
Any ideas? basically i want to extract from the same table all the sequences that are different betweent two timestamp values. With a colum "added" which represents if a row is new or if a row has been deleted.
Source table:
score_timestamp sequence
1292239056000 0
1292239056000 1
1292239056000 2
1292238452000 1
1292238452000 2
1292238452000 3
Example between (1292239056000, 1292238452000)
Query result (2 rows):
sequence added
3 1
0 0
Example between (1292238452000, 1292239056000)
Query result (2 rows):
sequence added
0 1
3 0
Example between (1292239056000, 1292239056000)
Query result (0 rows):
sequence added
This query gets all sequences that appear only once within both timestamps, and checks if it occurs for the first or for the second timestamp.
SELECT
sequence,
CASE WHEN MIN(score_timestamp) = 1292239056000 THEN 0 ELSE 1 END AS added
FROM scoredtable
WHERE score_timestamp IN ( 1292239056000, 1292238452000 )
AND ( 1292239056000 <> 1292238452000 ) -- No rows, when timestamp is the same
GROUP BY sequence
HAVING COUNT(*) = 1
It returns your desired result:
sequence added
3 1
0 0
Given two timestamps
SET #ts1 := 1292239056000
SET #ts2 := 1292238452000
you can get your additions and deletes with:
SELECT s1.sequence AS sequence, 0 as added
FROM scoredtable s1 LEFT JOIN
scoredtable s2 ON
s2.score_timestamp = #ts2 AND
s1.sequence = s2.sequence
WHERE
s1.score_timestamp = #ts1 AND
s2.score_timestampe IS NULL
UNION ALL
SELECT s2.sequence, 1
FROM scoredtable s1 RIGHT JOIN
scoredtable s2 ON s1.score_timestamp = #ts1 AND
s1.sequence = s2.sequence
WHERE
s2.score_timestamp = #ts2 AND
s1.score_timestampe IS NULL
depending on the number of rows and the statistics the above query might perform better then group by and having count(*) = 1 version (i think that will always need full table scan, while the above union should be able to do 2 x anti-join which might fare better)
If you have substantial data set, do let us know which is faster (test with SQL_NO_CACHE for comparable results)