What is output of this query and Why? - sql

declare #table as table
(
id int identity(1,1), salary int
)
insert into #table values(1000)
insert into #table values(2000)
insert into #table values(5000)
insert into #table values(4000)
insert into #table values(1000)
insert into #table values(8000)
insert into #table values(9000)
insert into #table values(6000)
insert into #table values(1000)
insert into #table values(7000)
insert into #table values(3000)
select
A.salary
from
#table as A
where
(select count(*)
from #table B
where b.salary < A.salary) > 5

It will display the salary which is greater than 5000

In the query, you are self joining a table.
And You are counting number of records from inner table which are higher than current record of outer table.
So it will give you 5 records from table A which are higher than 5 records from inner query
Sub Query select count(*) from #table B where b.salary < A.salary will give you number of records which are less in salary than of table A
And So the result.

your query gonna return
5000
8000
9000
6000
7000
because as per your query it will check if a record is greater then 6 other records of the table or not.
as the inner query counting it for you.
if you remove all entries of 1000 the result will change to
8000
9000
because now only these two records are greater then 6 other records of the tables.

Related

Get result based on Row_number criteria

I have tables three tables: #T, #T1, #Maintable, for which I'll attach the corresponding DDL.
Create table #T ( id int , reason varchar (50));
insert into #T Values (1,'Texas for live music');
insert into #T Values (1,'Texas for BBQ');
insert into #T Values (2,'Wisconsin for dairy products');
insert into #T Values (2,'Wisconsin for Rock');
insert into #T Values (2,'Wisconsin for Bird');
insert into #T Values (3,'North Carolina for Pepsi');
insert into #T Values (4,'Missouri for Forest');
insert into #T Values (5,'Oklohoma for cowboy');
insert into #T Values (5,'Oklohoma for Native Americans');
insert into #T Values (5,'Oklohoma for oil and gas');
Create table #T1 ( id int , reason varchar (50));
insert into #T1 Values (1,'Texas for live music,BBQ');
insert into #T1 Values (2,'Wisconsin for dairy products, rock,bird');
insert into #T1 Values (3,'North Carolina for Pepsi');
insert into #T1 Values (4,'Missouri for Forest');
insert into #T1 Values (5,'Oklohoma for Native Americans,oil and gas');
Create table #MainTable (id int, State varchar(20),Capital varchar(30),Governer varchar(30));
Insert into #Maintable values (1,'Texas','Austin','A');
Insert into #Maintable values (2,'Wisconsin','Madison','B');
Insert into #Maintable values (3,'North Carolina','Releigh','C');
Insert into #Maintable values (4,'Missouri','Jefferson City','D');
Insert into #Maintable values (5,'Oklohoma','Oklohoma city','E');
Expected Output
ID
Reason
State
Capital
Governer
1
Texas for live music,BBQ
Texas
Austin
A
2
Wisconsin for dairy products, rock,bird
Wisconsin
Madison
B
3
North Carolina for Pepsi
North Carolina
Releigh
C
4
Missouri for Forest
Missouri
Jefferson City
D
5
Oklohoma for Native Americans,oil and gas
Oklohoma
Oklohoma city
E
I have a couple of tables, based on the criteria I will be filtering records from table #T and joining with other tables to get more columns but with the help of cte, I am not able to filter. if first table #T has more than one Id then we will be using reason from another table #T1. If it has only one Id then we will be using Reason from Table #T and finally, we will join with #main table to get other records. I have added an image describing more. Help is much appreciated. All those temp tables we can test
And the scenario is:
If reason appears more than once in #T table use #T1 table
If reason appears only once in the #T table use #T only, this is the first table
Here's my coding attempt:
with cte as (
select *, ROW_NUMBER() over (partition by id order by id) rn
from #T
)
select mt.id, state, capital, Governer,
case when c.rn > 1
then #t1.reason
else c.reason
end as reason
from cte c
join #t1 on c.id = #t1.id
join #maintable mt on c.id = mt.id
I am getting more results, I was expecting only 5 records. I guess there is some issue in my row_number. Help is appreciated.
You should first extract the ranking value from the table "#T" for each ID. Then you can use the COALESCE function in combination with LEFT JOIN operations to solve your problem:
WITH #T_ranked AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY reason) AS rn
FROM #T
)
SELECT m.id,
COALESCE(#T1.reason, #T_ranked.reason) AS Reason,
m.State,
m.Capital,
m.Governer
FROM #Maintable m
LEFT JOIN #T1 ON m.id = #T1.id
LEFT JOIN #T_ranked ON m.id = #T_ranked.id AND #T_ranked.rn = 1
The LEFT JOIN operations will make your ids in the "MainTable" be kept, while the COALESCE function will make the first non-null argument to be the value for the "Reason" new field, in the specific case it will check whether "#T1.Reason" is null, if it is then it will assign "#T2.Reason". Values from "#T.Reason" which have an existing correspondent in "#T1.Reason" will never be selected in this way.
Check the demo here.

Add extra rows in result set of query with dummy data in sql server

I have this table let's say Students. If there are 2 students in table and result set gives 2 rows than I want to add 5 more rows with dummy data in it no matter what it says i.e 'Dummy Record' or something.
SELECT FirstName, (SELECT COUNT(*) FROM Student) Total
FROM Student
If the output is like this from above query.
FirstName Total
Isaac Frempong 2
Erick Ortiz 2
I want the output to be like this
FirstName Total
Isaac Frempong 2
Erick Ortiz 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
I hope it's achievable, I'm unable to figure how to apply CASE or IF statements here. Maybe somebody could help.
One method is to use a VALUES() statement to construct the rows:
SELECT FirstName, COUNT(*) OVER () as Total
FROM Student
UNION ALL
SELECT 'Dummy', (SELECT COUNT(*) FROM Student)
FROM (VALUES (1), (2), (3), (4), (5)) v(n);
EDIT:
If you always want 7 rows, use arithmetic:
SELECT FirstName, COUNT(*) OVER () as Total
FROM Student
UNION ALL
SELECT 'Dummy', s.total
FROM (VALUES (1), (2), (3), (4), (5), (6), (7)) v(n) CROSS JOIN
(SELECT COUNT(*) as total FROM Student) s
WHERE v.n <= 7 - s.total;
s
Here is a code working on a table varaibale I called #students
Please replace it by your own table
I have filled this table with some Data, and tested the different cases (records number less then 7 or more than 7, and worked as you wish !)
1) First create a variable table called #mytable which will be filled by n records
n will be 7 if the number of records in your students table is equal or less than 7
n will be the number of records in your student table if it is more than 7
2) Then make a right outer join between your student table to which you add the record number using CTE ,row_number() function.
declare #students as table(id int identity(1,1),firstname nvarchar(50))
insert into #students(firstname) values
('Ali ben Hassine'),
('Mohamed el Aabed'),
('Ali ben Hassine'),
('Mohamed el Aabed'),
('Mohamed el Aabed'),
('Tahar Harbi'),
('Hassine Ayari'),
('Ihsen Trabelsi'),
('Marwa Mostari'),
('Mourad Zmerli'),
('Hafedh Gabsi'),
('Miloud Filali');
declare #mytab as table(n int)
declare #n as int
select #n=count(distinct(firstname)) from #students
if #n<7 set #n=7
declare #i as int
set #i=1
while #i <#n+1
begin
insert into #mytab values(#i)
set #i=#i+1
end;
with cte as
(select row_number() over(partition by 1 order by firstname) r#,firstname,count(1) Total from #students group by firstname)
select isnull(cte.firstname,'Dummy') firstanme,isnull(cte.total,2) Total from cte
right outer join #mytab t2 on cte.r#=t2.n
[![enter image description here][1]][1]

Turn value into singular row

Current
Name Quantity
---------------
Stella 2
Jennifer 2
Greg 3
Requested result
Name Quantity
---------------
Stella 1
Stella 1
Jennifer 1
Jennifer 1
Greg 1
Greg 1
Greg 1
How should I do it?
declare #T table
(
Name varchar(50),
Sales int
)
insert into #T values
('Stella', '2'),
('Jennifer', '2'),
('Greg', '3')
If the maximum value in the quantity column is known to be less than 32,767, you can use Recursion to generate numbers and join the Numbers to achieve your result.
/*******************************************
Max Recursion Count in SQL Server is 32767
Limitation of 32767 Numbers!
******************************************/
;WITH Numbers (Number) AS
(
SELECT 1
UNION ALL
SELECT 1 + Number FROM Numbers WHERE Number < 100
)
SELECT m.Name,
Quantity = 1
FROM MyTable m
JOIN #numbers n ON m.Quantity <= n.Number
OPTION (MAXRECURSION 32767);
Using recursion and borrowing Michael Fredrickson's setup code:
declare #T table (
Name varchar(50),
Sales int
)
insert into #T values ('Stella', '2')
insert into #T values ('Jennifer', '2')
insert into #T values ('Greg', '3')
-- Recursive verion
;with People (Name, Sales) as
(
select Name, Sales
from #T
union all
select Name, Sales - 1
from People
where Sales - 1 > 0
)
select Name, 1 as Quantity
from People
option (maxrecursion 0) -- Recurse without limit
This seems to run faster on my box (5x faster than Michael Fredrickson's according to query plan, but with many more logical reads), not that it matters much.
You'll probably want to have a pre-populated numbers table to do this:
declare #T table (
Name varchar(50),
Sales int
)
declare #numbers table (
Number int
)
insert into #numbers values (1)
insert into #numbers values (2)
insert into #numbers values (3)
insert into #numbers values (4)
-- Etc... up to however many numbers is the max possible value for sales...
insert into #T values ('Stella', '2')
insert into #T values ('Jennifer', '2')
insert into #T values ('Greg', '3')
SELECT
t.Name,
1 AS Sales
FROM
#T t JOIN
#numbers n ON
t.Sales >= n.Number
ORDER BY t.Name
That's how you could do it, but I'm not sure on why you would want to do it.

SQL Query to return rows where a list of numbers is between start and end values

There is a table in Oracle with the columns:
id | start_number | end_number
---+--------------+------------
1 | 100 | 200
2 | 151 | 200
3 | 25 | 49
4 | 98 | 99
5 | 49 | 100
There is a list of numbers (50, 99, 150).
I want an sql statement that returns all the ids where any of the numbers in the list of numbers is found equal to or between the start_number and the end_number.
Using the above example; 1, 4 and 5 should be returned.
1 - 150 is between or equal to 100 and 200
2 - none of the numbers are between or equal to 151 and 200
3 - none of the numbers are between or equal to 25 and 49
4 - 99 is between or equal to 98 and 99
5 - 50 and 99 are between or equal to 49 and 100
drop table TEMP_TABLE;
create table TEMP_TABLE(
THE_ID number,
THE_START number,
THE_END number
);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (1, 100, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (2, 151, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (3, 25, 49);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (4, 98, 99);
insert into TEMP_TABLE(the_id, the_start, the_end) values (5, 49, 100);
The following is the solution I came up with based on the comments and answers below plus some additional research:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(SYS.DBMS_DEBUG_VC2COLL(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
This works too:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(sys.ku$_vcnt(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
Here is a full example:
create table #list (
number int
)
create table #table (
id int,
start_number int,
end_number int
)
insert into #list values(50)
insert into #list values(99)
insert into #list values(150)
insert into #table values(1,100,200)
insert into #table values(2,151,200)
insert into #table values(3,25,49)
insert into #table values(4,98,99)
insert into #table values(5,49,100)
select distinct a.* from #table a
inner join #list l --your list of numbers
on l.number between a.start_number and a.end_number
drop table #list
drop table #table
You'll simply need to remove the code about #table (create, insert and drop) and put your table in the select.
It partly depends on how your are storing your list of numbers. I'll assume that they're in another table for now, as even then you have many options.
SELECT
*
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourList WHERE number BETWEEN yourTable.start_number AND yourTable.end_number)
Or...
SELECT
*
FROM
yourTable
INNER JOIN
yourList
ON yourList.number BETWEEN yourTable.start_number AND yourTable.end_number
Both of those are the simplest expressions, and work well for small data sets. If your list of numbers is relatively small, and your original data is relatively large, however, this may not scale well. This is because both of the above scan the whole of yourTable and then check each record against yourList.
What may be preferable is to scan the list, and then attempt to use indexes to check against the original data. This would require you to be able to reverse the BETWEEN statement to yourTable.start_number BETWEEN x and y
This can only be done if you know the maximum gap between start_number and end_number.
SELECT
*
FROM
yourList
INNER JOIN
yourTable
ON yourTable.end_number >= yourList.number
AND yourTable.start_number <= yourList.number
AND yourTable.start_number >= yourList.number - max_gap
To achieve this I would store the value of max_gap in another table, and update it as the values in yourTable change.
You will want to create a temporary table to hold your numbers, if the numbers aren't already in one. Then it becomes relatively simple:
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN TempTable tt --your list of numbers
ON tt.number Between mt.start_number and mt.end_number
To create the table based on an array of passed values, you can use table definitions in your procedure. I'm light on Oracle syntax and don't have TOAD handy, but you should be able to get something like this to work:
CREATE OR REPLACE PROCEDURE FindIdsFromList
AS
DECLARE
TYPE NumberRecord IS RECORD (Number int NOT NULL)
TYPE NumberList IS TABLE OF NumberRecord;
NumberList myNumberList;
BEGIN
myNumberList := (50,99,150);
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN myNumberList nt --your list of numbers
ON nt.Number Between mt.start_number and mt.end_number
END

TSQL- generate a sequence number for duplicate records

Using SQL Server 2000, consider a source table with more than 400,000 records.
The task is to select each regno entry with an incrementing on-the-fly rowid or sequence number for those with duplicates or multiple entries. For those which do NOT have duplicate entries in the source table, the rowid should simply be null.
Here's an example of the desired output:
regno rowid
100 1
100 2
100 3
200 null
300 4
300 5
400 null
500 null
600 6
600 7
Question:
What query would do the desired sequence incrementing using TSQL in SQL Server 2000?
If my comment is correct (600 should be 6,7) then have a look at this
DECLARE #Table TABLE(
regno INT,
rowid INT
)
INSERT INTO #Table (regno,rowid) SELECT 100, NULL
INSERT INTO #Table (regno,rowid) SELECT 100, NULL
INSERT INTO #Table (regno,rowid) SELECT 100, NULL
INSERT INTO #Table (regno,rowid) SELECT 200, NULL
INSERT INTO #Table (regno,rowid) SELECT 300, NULL
INSERT INTO #Table (regno,rowid) SELECT 300, NULL
INSERT INTO #Table (regno,rowid) SELECT 400, NULL
INSERT INTO #Table (regno,rowid) SELECT 500, NULL
INSERT INTO #Table (regno,rowid) SELECT 600, NULL
INSERT INTO #Table (regno,rowid) SELECT 600, NULL
DECLARE #TempTable TABLE(
ID INT IDENTITY(1,1),
regno INT
)
INSERT INTO #TempTable (regno)
SELECT regno
FROM #Table
SELECT regno,
CASE
WHEN (SELECT COUNT(1) FROM #TempTable WHERE regno = t.regno) = 1
THEN NULL
ELSE (SELECT COUNT(1) FROM #TempTable WHERE regno = t.regno) - (SELECT COUNT(1) FROM #TempTable WHERE regno = t.regno AND ID > t.ID) +
(SELECT COUNT(1) FROM #TempTable WHERE regno < t.regno AND regno IN (SELECT regno FROM #TempTable GROUP BY regno having COUNT(1) > 1))
END Val
FROM #TempTable t
The query to extract the non-unique records would be
select regno,count(*) from table group by regno having count(*) > 1
I don't know enough about MSSQL to tell you how to generate an incrementing sequence number to update the records that match the query.
Without a temp table:
DECLARE #Table TABLE(
regno INT
)
INSERT INTO #Table (regno) SELECT 100
INSERT INTO #Table (regno) SELECT 100
INSERT INTO #Table (regno) SELECT 100
INSERT INTO #Table (regno) SELECT 200
INSERT INTO #Table (regno) SELECT 300
INSERT INTO #Table (regno) SELECT 300
INSERT INTO #Table (regno) SELECT 400
INSERT INTO #Table (regno) SELECT 500
INSERT INTO #Table (regno) SELECT 600
INSERT INTO #Table (regno) SELECT 600
select regno, null as rowid from #Table group by regno having count(*) = 1
union
select regno, row_number() OVER (ORDER BY a.regno) as rowid
from #table a
where regno in (select regno from #table group by regno having count(*) > 1)
regno rowid
----------- --------------------
100 1
100 2
100 3
200 NULL
300 4
300 5
400 NULL
500 NULL
600 6
600 7
Oops - did not see that you want to do this in SQL 2000 until after posting this ... ignore my query please. In SQL 2000 you need a temp table to generate the sequence.