SQL Select Condition Question - sql

I have a quick question about a select statement condition.
I have the following table with the following items. What I need to get is the object id that matches both type id's.
TypeId ObjectId
1 10
2 10
1 11
So I need to get both object 10 because it matches type id 1 and 2.
SELECT ObjectId
FROM Table
WHERE TypeId = 1
AND TypeId = 2
Obviously this doesn't work because it won't match both conditions for the same row. How do I perform this query?
Also note that I may pass in 2 or more type id's to narrow down the results.

Self-join:
SELECT t1.ObjectId
FROM Table AS t1
INNER JOIN Table AS t2
ON t1.ObjectId = t2.ObjectId
AND t1.TypeId = 1
AND t2.TypeId = 2
Note sure how you want the behavior to work when passing in values, but that's a start.

I upvoted the answer from #Cade Roux, and that's how I would do it.
But FWIW, here's an alternative solution:
SELECT ObjectId
FROM Table
WHERE TypeId IN (1, 2)
GROUP BY ObjectId
HAVING COUNT(*) = 2;
Assuming uniqueness over TypeId, ObjectId.
Re the comment from #Josh that he may need to search for three or more TypeId values:
The solution using JOIN requires a join per value you're searching for. The solution above using GROUP BY may be easier if you find yourself searching for an increasing number of values.

This code is written with Oracle in mind. It should be general enough for other flavors of SQL
select t1.ObjectId from Table t1
join Table t2 on t2.TypeId = 2 and t1.ObjectId = t2.ObjectId
where t1.TypeId = 1;
To add additional TypeIds, you just have to add another join:
select t1.ObjectId from Table t1
join Table t2 on t2.TypeId = 2 and t1.ObjectId = t2.ObjectId
join Table t3 on t3.TypeId = 3 and t1.ObjectId = t3.ObjectId
join Table t4 on t4.TypeId = 4 and t1.ObjectId = t4.ObjectId
where t1.TypeId = 1;
Important note: as you add more joins, performance will suffer a LOT.
In regards to Bill's answer you can change it to the following to get rid of the need to assume uniqueness:
SELECT ObjectId
FROM (SELECT distinct ObjectId, TypeId from Table)
WHERE TypeId IN (1, 2)
GROUP BY ObjectId
HAVING COUNT(*) = 2;
His way of doing it scales better as the number of types gets larger.

Try this
Sample Input:(Case 1)
declare #t table(Typeid int,ObjectId int)
insert into #t
select 1,10 union all select 2,10 union all
select 1,11
select * from #t
Sample Input:(Case 2)
declare #t table(Typeid int,ObjectId int)
insert into #t
select 1,10 union all select 2,10 union all
select 3,10 union all select 4,10 union all
select 5,10 union all select 6,10 union all
select 1,11 union all select 2,11 union all
select 3,11 union all select 4,11 union all
select 5,11 union all select 1,12 union all
select 2,12 union all select 3,12 union all
select 4,12 union all select 5,12 union all
select 6,12
select * from #t
Sample Input:(Case 3)[Duplicate entries are there]
declare #t table(Typeid int,ObjectId int)
insert into #t
select 1,10 union all select 2,10 union all
select 1,10 union all select 2,10 union all
select 3,10 union all select 4,10 union all
select 5,10 union all select 6,10 union all
select 1,11 union all select 2,11 union all
select 3,11 union all select 4,11 union all
select 5,11 union all select 1,12 union all
select 2,12 union all select 3,12 union all
select 4,12 union all select 5,12 union all
select 6,12 union all select 3,12
For case 1, the output should be 10
For case 2 & 3, the output should be 10 and 12
Query:
select X.ObjectId from
(
select
T.ObjectId
,count(ObjectId) cnt
from(select distinct ObjectId,Typeid from #t)T
where T.Typeid in(select Typeid from #t)
group by T.ObjectId )X
join (select max(Typeid) maxcnt from #t)Y
on X.cnt = Y.maxcnt

Related

SQL Get First Non Null Value By Date From A Table

Im trying a CASE expression, something like this:
LEFT OUTER JOIN table_1 AS T1
ON T2.common_id = T1.common_id
CASE WHEN T1.column_1 IS NOT NULL
THEN T1.column_1
WHEN T1.column_1 IS NULL
THEN get first value from T1.column that is not null by date
ELSE 0
END
Its the logic for that 6th line which I have written out what I want that I can't quite get right.
THEN get first value from T1.column that is not null by date
I have been looking at the FIRST_VALUE function but can't quite figure it out correctly. But there are maybe other ways to get it to work.
you can to find FIRST_VALUE that is not null with use this query
First use order by date for find date is null
after get value FIRST_VALUE date with FIRST_VALUE
select *,FIRST_VALUE(date) over(partition by T1.common_id order by case when date is not null then 1 else 2 end)
from #table_2 T2
LEFT OUTER JOIN #table_1 AS T1
ON T2.common_id = T1.common_id
You can to insert the basic data with the following codes
create table #table_1 (id int,common_id int,date datetime)
create table #table_2 (id int,common_id int,name varchar(100))
insert into #table_1(id,common_id,date)
select 1,1,'2015-05-24' union select 2,1, null union select 3,1, '2014-09-01' union
select 4,4,null union select 5,4, '2019-08-05' union select 6,4, '2000-09-07' union
select 7,7,null union select 8,7, '2019-08-05' union
select 9,12,'2019-08-06' union select 10,12, '2019-08-05' union
select 11,18,'2019-08-06' union select 12,19, '2019-08-05'
insert into #table_2(id,common_id,name)
select 1,1,'a' union select 2,1, null union select 3,1, 'b' union
select 4,4,'k' union select 5,4, 't' union select 6,4, 'c' union
select 7,7,'aaa' union select 8,7, 'sada' union
select 9,12,'44dd' union select 9,12, '44'

SQL Select query optimization with indexing

A posts table contains 1 million rows. This table has a field with the name poster_id.
I have a list of followers by this poster_id.
I am trying to get a list of all activities from this followers (35 in this case but less or more is possible) in the last 48 hours.
I use this query:
SELECT post_id
, topic_id
, poster_id
, post_time
FROM posts
WHERE post_time > 1606833542
AND poster_id IN (80202, 74247, 79290, 72488,
111751, 85040, 100256, 68025,
101088, 101598, 101950, 103252,
103071, 80063, 100372, 102530, 109961,
109854, 105626, 108967, 110391, 104423,
113243, 111673, 113979, 104670, 127318,
68252, 109606, 121393, 122991, 124489,
127723, 126525)
ORDER
by post_time
LIMIT 100
Problem:
This query takes too long (0.4000 seconds) to execute.
The poster_id has an index of the post table.
How can I make this query faster?
try avoid the IN clause and use a join
SELECT
p.post_id
, p.topic_id
, p.poster_id
, p.post_time
FROM posts p
INNER JOIN (
SELECT 80202 poster_id
UNION SELECT 74247
UNION SELECT 79290
UNION SELECT 72488
UNION SELECT 111751
UNION SELECT 85040
UNION SELECT 100256
UNION SELECT 68025
UNION SELECT 101088
UNION SELECT 101598
UNION SELECT 101950
UNION SELECT 103252
UNION SELECT 103071
UNION SELECT 80063
UNION SELECT 100372
UNION SELECT 102530
UNION SELECT 109961
UNION SELECT 109854
UNION SELECT 105626
UNION SELECT 108967
UNION SELECT 110391
UNION SELECT 104423
UNION SELECT 113243
UNION SELECT 111673
UNION SELECT 113979
UNION SELECT 104670
UNION SELECT 127318
UNION SELECT 68252
UNION SELECT 109606
UNION SELECT 121393
UNION SELECT 122991
UNION SELECT 124489
UNION SELECT 127723
UNION SELECT 126525
) t ON t.poster_id = p.poster_id
AND p.post_time > 1606833542
ORDER by p.post_time LIMIT 100
could be the value in the IN clause are form some subquery in this case ypou could use the related subquery instead of the UNION .....
WHERE IN clase is the same as serverl OR condition (several internal subquery) .. instead INNER JOIN just check the values in a single operation
for better performance, instead of you actual index on poster_id, you could try using a redundant index adding all the columns you select in your index eg:
create index my_index on posts (poster_id, post_id, topic_id , post_time )
in this way all the query value are obtained using the index and the query don't need to access at the table ..

SQL UNION OR JOIN 2 SELECTION

I will made a simple exemple to understand better what I'm trying to do:
I have Selection 1 witch takes data from 4 tables with severals inner join. The results is like image shown bellow:
I have Selection 2 witch takes data from another 4 tables.
The only difference between this 2 results of the selections is the column 4.
The results is like image shown bellow:
The results I want is like this:
If I make UNION ALL he put the values underneath (the result is with 20 rows), and I don't need like that.
If I Make Join between them I have 10x10=100 rows.
EXEMPLE LINK: SQLFiddle
Get data from both selection with inner joins and display only required columns from both selections
;WITH CTE1
AS
(
SELECT * FROM SELECTION1
)
,CTE2
AS
(
SELECT * FROM SELECTION2
)
SELECT
CTE1.UNAME,
CTE1.PRE_NAME,
CTE1.TIME,
CTE1.FIRST_VALUE,
CTE2.FINAL_VALUE
FROM CTE1
INNER JOIN CTE2
ON CTE1.UNAME=CTE2.UNAME
AND CTE1.PRE_NAME=CTE2.PRE_NAME
AND CTE1.TIME=CTE2.TIME
UPDATE
FIDDLE
I have tried to get something unique from both tables so used ROW_NUMBER(). This might help. Joins with duplicate values in both tables will make it 10x10 rows only which you don't want.
WITH CTE1
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY U_NAME) AS NO, * FROM SELECTION1
)
,CTE2
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY U_NAME) AS NO,* FROM SELECTION2
)
SELECT
CTE1.U_NAME,
CTE1.PRE_NAME,
CTE1.TIME,
CTE1.FIRST_VALUE,
CTE2.FINAL_VALUE
FROM CTE1
INNER JOIN CTE2
ON CTE1.NO=CTE2.NO
UPDATE 2
WITH CTE1
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY UD_NAME) AS NO, * FROM
(
SELECT R0507UNIT_.R0507NAME_ AS UD_NAME,
R0101MAPAR.R0101NAME_ AS PRE_NAME,
TO_CHAR(D0903GSCHH.D0903TFROM + INTERVAL '2' HOUR, 'DD.MM.YYYY HH24:MI:SS') AS TIME_FROM,
D0904GSCHI.D0904POWER AS FINAL_POWER
FROM D0904GSCHI
INNER JOIN D0903GSCHH
ON
D0904GSCHI.D0903TFROM = D0903GSCHH.D0903TFROM
AND D0904GSCHI.D0903ID___ = D0903GSCHH.D0903ID___
INNER JOIN R0101MAPAR
ON
D0903GSCHH.R0101ID___ = R0101MAPAR.R0101ID___
INNER JOIN R0507UNIT_
ON
D0903GSCHH.R0507ID___=R0507UNIT_.R0507ID___
WHERE D0903GSCHH.D0903TFROM >= TO_DATE('07.02.2017 20:00','DD.MM.YYYY HH24:MI:SS')
AND D0903GSCHH.D0903TFROM < TO_DATE('08.02.2017 20:00','DD.MM.YYYY HH24:MI:SS')
--AND R0507UNIT_.R0507NAME_ = 'BUCV3'
) AS A --THIS IS AN ALIAS
)
SELECT A.U_NAME,
A.PRE_NAME,
A.TIME,
A.FIRST_VALUE,
B.FINAL_VALUE
FROM Selection1 AS A
INNER JOIN Selection2 AS B
ON A.U_NAME = B.U_NAME
AND A.PRE_NAME = B.PRE_NAME
AND A.TIME = B.TIME
You need to join the two queries together using an INNER JOIN:
SELECT s1.U_NAME
,s1.PRE_NAME
,s1.TIME
,s1.FIRST_VALUE
,s2.FINAL_VALUE
FROM (
-- First query
SELECT U_NAME, PRE_NAME, TIME, FIRST_VALUE
FROM Selection1
) s1
INNER JOIN (
-- Second query
SELECT U_NAME, PRE_NAME, TIME, FINAL_VALUE
FROM Selection1
) s2
ON s1.U_NAME = s2.U_NAME
AND s1.PRE_NAME = s2.PRE_NAME
AND s1.TIME = s2.TIME
;
If I consider the two inputs as tables, you can achieve the desired results with another join between the these tables (But i will not suggest it due to performance issues).
SELECT
TBL1.U_NAME,TBL1.PRE_NAME,TBL1.TIME,TBL1.FIRST_VALUE,TBL2.FINAL_VALUE FROM
FROM FIRSTTABLE TBL1 INNER JOIN SECONDTABLE TBL2 ON TBL1.U_NAME=TBL2.U_NAME
AND TBL1.PRE_NAME=TBL2.PRE_NAME AND TBL1.TIME=TBL2.TIME
I see your already have some inner joins to get data in FirstTable and SecondTable. If both of these queries are using common tables to bring data, you can do it in a single query also. If I am understanding your problem correctly, Union is not required for the kind of resultset you are looking for.
If you can provide the complete query it would give more context and can provide a better solution.
BEGIN TRAN
CREATE TABLE #temp (U_Name Nvarchar(20),PRE_Name Nvarchar(20),Time Datetime,firstValue int)
CREATE TABLE #temp1 (U_Name Nvarchar(20),PRE_Name Nvarchar(20),Time Datetime,firstValue int)
INSERT INTO #temp
Select 'GV','NMOP','10.02.2017', 90 UNION ALL
Select 'GV','NMOP','10.02.2017', 100 UNION ALL
Select 'GV','NMOP','10.02.2017', 120 UNION ALL
Select 'GV','NMOP','10.02.2017', 300 UNION ALL
Select 'GV','NMOP','10.02.2017', 200 UNION ALL
Select 'GV','NMOP','10.02.2017', 100 UNION ALL
Select 'GV','NMOP','10.02.2017', 50 UNION ALL
Select 'GV','NMOP','10.02.2017', 60 UNION ALL
Select 'GV','NMOP','10.02.2017', 70 UNION ALL
Select 'GV','NMOP','10.02.2017', 10
INsert into #temp1
Select 'GV','NMOP','10.02.2017', 60 UNION ALL
Select 'GV','NMOP','10.02.2017', 52 UNION ALL
Select 'GV','NMOP','10.02.2017', 10 UNION ALL
Select 'GV','NMOP','10.02.2017', 300 UNION ALL
Select 'GV','NMOP','10.02.2017', 33 UNION ALL
Select 'GV','NMOP','10.02.2017', 100 UNION ALL
Select 'GV','NMOP','10.02.2017', 50 UNION ALL
Select 'GV','NMOP','10.02.2017', 56 UNION ALL
Select 'GV','NMOP','10.02.2017', 99 UNION ALL
Select 'GV','NMOP','10.02.2017', 10
SELECT ROW_NUMBER() OVER (ORDER BY U_NAME) AS Rowno,a.U_Name, a.PRE_Name,a.Time,a.firstValue into #t from #temp a
SELECT ROW_NUMBER() OVER (ORDER BY U_NAME) AS Rowno, b.U_Name, b.PRE_Name,b.Time,b.firstValue as finalValue INTO #TT from #temp1 b
SELECT a.U_Name,a.PRE_Name,a.Time,a.firstValue, b.finalValue
FROM #t a
INNER JOIN #TT b ON a.Rowno=b.Rowno AND a.U_Name=b.U_Name AND a.PRE_Name=b.PRE_Name AND a.time=b.Time
ROLLBACK TRAN

Exists - Not exists - Exclude records those are having status in 0 ignoring other status associated with that record

Below is my data.
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
I want to get only B, C, D as output from the query. Lets say, 0 is status-complete & I want to ignore records associated with it.
This I am able to achieve using the not in clause as below.
select * from cte c
where c.name not in (select cf.name from cte cf where cf.status=0)
But I want to achieve this using exists or not exists clause in where condition.
Could you please share the logic ?
thanks,
Can you please try with this:
SELECT * FROM cte c
WHERE NOT EXISTS (SELECT cf.name
FROM cte cf WHERE c.name = cf.name AND cf.status = 0)
For this we don't need any column in the where clause because we are addressing that conditional column as comparison in WHERE of sub query.
Please try this
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
Select * from cte c
where NOT EXISTS (select 1 from cte cf where cf.status=0 AND c.name = cf.name)
With NOT EXISTS
with cte as(
select 'A' name, 0 status
union all select 'A' name, 1 status
union all select 'B' name, 1 status
union all select 'C' name, 2 status
union all select 'D' name, 1 status
)
select * from cte out where NOT EXISTS
(select inn.name from cte inn WHERE out.name = inn.name and inn.status=0)
DECLARE #tbl1 AS TABLE
(
Name VARCHAR(50),
Status INT
)
INSERT INTO #tbl1 VALUES('A',0)
INSERT INTO #tbl1 VALUES('A',1)
INSERT INTO #tbl1 VALUES('B',1)
INSERT INTO #tbl1 VALUES('C',1)
INSERT INTO #tbl1 VALUES('D',1)
INSERT INTO #tbl1 VALUES('E',0)
With Not EXISTS:
SELECT
*
FROM #tbl1 T1
WHERE NOT EXISTS( SELECT T2.Name FROM #tbl1 T2 WHERE T2.Status=0 AND T1.Name=T2.Name)
With EXISTS:
SELECT
*
FROM #tbl1 T1
WHERE EXISTS( SELECT T2.Name FROM #tbl1 T2 WHERE T1.Name=T2.Name AND T1.Status=1 GROUP BY T2.Name having count(T2.Status)=1 )
Output:

How to avoid Sorting in Union ALL

MY question is simple, How do you avoid the automatic sorting which the UNION ALL query does?
This is my query
SELECT * INTO #TEMP1 FROM Final
SELECT * INTO #TEMP2 FROM #TEMP1 WHERE MomentId = #MomentId
SELECT * INTO #TEMP3 FROM #TEMP1 WHERE RowNum BETWEEN #StartRow AND #EndRow
SELECT * INTO #TEMP4 FROM (SELECT *FROM #TEMP3 UNION ALL SELECT *FROM #TEMP2) as tmp
SELECT DISTINCT * FROM #TEMP4
I'm using SQL Server 2008. I need the Union ALL to perform like a simple Concatenate, which it isn't! Appreciate your help in this.
I think you're mistaken on which operation is actually causing the sort. Check the code below, UNION ALL will not cause a sort. You may be looking at the DISTINCT operation, which uses a sort (it sorts all items and the eliminates duplicates)
CREATE TABLE #Temp1
(
i int
)
CREATE TABLE #temp2
(
i int
)
INSERT INTO #Temp1
SELECT 3 UNION ALL
SELECT 1 UNION ALL
SELECT 8 UNION ALL
SELECT 2
INSERT INTO #Temp2
SELECT 7 UNION ALL
SELECT 1 UNION ALL
SELECT 5 UNION ALL
SELECT 6
SELECT * INTO #TEMP3
FROM (SELECT * FROM #Temp1 UNION ALL SELECT * FROM #temp2) X
UNION ALL adds all the records where as UNION adds only new/distinct records.
Since you are using UNION ALL and using DISTINCT soon after, I think you are looking for UNION
SELECT * INTO #TEMP4 FROM
(
SELECT * FROM #TEMP3
UNION --JUST UNION
SELECT * FROM #TEMP2
) AnotherTemp
Or you can simplify it as
SELECT * INTO #TEMP4 FROM
SELECT DISTINCT *
FROM Final
WHERE MomentId = #MomentId OR RowNum BETWEEN #StartRow AND #EndRow
I'm not familiar with SQL-Server, but you might get my idea
select *, 'A' tid, rownumber() tno from tableA
union all
select *, 'B', rownumber() from tableB
order by tid, tno;
This should get you all records of tableA in their specific order, followed by all records of tableB in their specific order.