SQL Where clause - sql

My application initially had a query similar to this one:-
SELECT column_name from PERSON
WHERE name in (list);
where list is comma separated list.
But, now the requirement has changed and i have to query the Persons table with name and age given.
I have the nameAgeList.
Initially, i thought a query similar to this would work (Create nameList and ageList from nameAgeList)
SELECT column_name from Person
WHERE name in (nameList)
AND age in (ageList)
But after carefully thinking, this seems to be a wrong query.
Please let me know how should I proceed ahead with this query.

Under Oracle, you can do this:
SELECT * FROM Person
WHERE
(name, age) IN (
('name1', age1),
('name2', age2)
-- Etc...
)
You can have up to 1000 tuples in this list.

One option is to create a temporary table (or if SQL Server, a table variable), place your names and ages in this table, and then simply join to it:
SELECT column_name from Person p
INNER JOIN myTempTable t ON t.Name = p.Name AND t.age = p.age

It's not pretty, and this one only works when you can generate your statement in code:
SELECT column from Person
WHERE 1=1
AND ( ( name = name1 and age = age1 )
OR ( name = name2 and age = age2 )
OR ( name = name3 and age = age3 )
OR ( name = name4 and age = age4 )
OR ( name = name5 and age = age5 )
... et cetera
)
Now, if you could put those lists into tables you could do alot better than this. Is there any way you can get those lists into the database? I assume that you really need some Person table that holds name and age for each individual.

Related

Pivot top 100 rows into columns for all tables in a database

My mission is to get top 100 rows from all my tables in a database, and union them into one result output.
Since the tables have varying columncount and varying types, the only way I see to do this is to put the row number as columns, and the column name in the first column. Row 1 from all tables will then be represented in column 2 and so forth.
My skills in SQL aren't good enough to figure this one out, so I hope the community can assist me in this.
Here is some code:
--Example table
create table Worker (Id int, FirstName nvarchar(max));
--Some data
insert into Worker values (1,'John'),(2,'Jane'),(3,'Elisa');
--Static pivot example
select * from (select
ROW_NUMBER() over (order by Id) as IdRows, FirstName from worker) as st
pivot
(
max(FirstName) for IdRows in ([1],[2],[3])
) as pt;
--Code to incorporate to get column name on column 1
select name from sys.columns where object_id('Worker') = object_id;
--Cleanup
drop table Worker;
I reckon the static pivot must be dynamic, which is not a problem. The above code is just to create a proof of concept, so I have something to build further on.
End result of query should be like this:
Column 1 2 3
FirstName John Erina Jane
I hope this can be solved without using cursors and temp tables, but maybe that's the way to go?
EDIT:
Forgot to mention, I'm using sqlserver (mssql)
EDIT2:
I'm not all that good at explaining, so here is some more code to do that job for me.
This will add another column to Worker table, and a query to show the desired result. It's the query that has to be "smarter" so it can handle future added tables and columns added. (again, this is a proof of concept. The database has > 200 tables and > 1000 columns)
--Add a column
alter table Worker add LastName nvarchar(max);
--Add some data
update Worker set LastName = 'Smith' where Id = 1;
update Worker set LastName = 'Smith' where Id = 2;
update Worker set LastName = 'Smith' where Id = 3;
--Query to give desired output (top 100, but only 3 rows in this table)
select 'FirstName' as 'Column',* from (select top 100
ROW_NUMBER() over (order by Id) as IdRows, FirstName from worker) as st
pivot
(
max(FirstName) for IdRows in ([1],[2],[3])
) as pt
union
select 'LastName' as 'Column',* from (select top 100
ROW_NUMBER() over (order by Id) as IdRows, LastName from worker) as st
pivot
(
max(LastName) for IdRows in ([1],[2],[3])
) as pt
To solve this, I used temp table, and put it into a loop. Not a good solution, but it works. Had to cast all the data to nvarchar to make it work.

SQL groupby multiple columns

Tablename: EntryTable
ID CharityName Title VoteCount
1 save the childrens save them 1
2 save the childrens saving childrens 3
3 cancer research support them 10
Tablename: ContestantTable
ID FirstName LastName EntryId
1 Neville Vyland 1
2 Abhishek Shukla 1
3 Raghu Nandan 2
Desired output
CharityName FullName
save the childrens Neville Vyland
Abhishek Shukla
cancer research Raghu Nandan
I tried
select LOWER(ET.CharityName) AS CharityName,COUNT(CT.FirstName) AS Total_No_Of_Contestant
from EntryTable ET
join ContestantTable CT
on ET.ID = CT.ID
group by LOWER(ET.CharityName)
Please advice.
Please have a look at this sqlfiddle.
Have a try with this query:
SELECT
e.CharityName,
c.FirstName,
c.LastName,
sq.my_count
FROM
EntryTable e
INNER JOIN ContestantTable c ON e.ID = c.EntryId
INNER JOIN (
SELECT EntryId, COUNT(*) AS my_count FROM ContestantTable GROUP BY EntryId
) sq ON e.ID = sq.EntryId
I assumed you actually wanted to join with ContestantTable's EntryId column. It made more sense to me. Either way (joining my way or yours) your sample data is faulty.
Apart from that, you didn't want repeating CharityNames. That's not the job of SQL. The database is just there to store and retrieve the data. Not to format it nicely. You want to work with the data on application layer anyways. Removing repeating data doesn't make this job easier, it makes it worse.
Most people do not realize that T-SQL has some cool ranking functions that can be used with grouping. Many things like reports can be done in T-SQL.
The first part of the code below creates two local temporary tables and loads them with data for testing.
The second part of the code creates the report. I use two common table expressions (CTE). I could have used two more local temporary tables or table variables. It really does not matter with this toy example.
The cte_RankData has two columns RowNum and RankNum. If RowNum = RankNum, we are on the first instance of charity. Print out the charity name and the total number of votes. Otherwise, print out blanks.
The name of the contestant and votes for that contestant are show on the detail lines. This is a typical report with sub totals show at the top.
I think this matches the report output that you wanted. I ordered the contestants by most votes descending.
Sincerely
John Miner
www.craftydba.com
--
-- Create the tables
--
-- Remove the tables
drop table #tbl_Entry;
drop table #tbl_Contestants;
-- The entries table
Create table #tbl_Entry
(
ID int,
CharityName varchar(25),
Title varchar(25),
VoteCount int
);
-- Add data
Insert Into #tbl_Entry values
(1, 'save the childrens', 'save them', 1),
(2, 'save the childrens', 'saving childrens', 3),
(3, 'cancer research', 'support them', 10)
-- The contestants table
Create table #tbl_Contestants
(
ID int,
FirstName varchar(25),
LastName varchar(25),
EntryId int
);
-- Add data
Insert Into #tbl_Contestants values
(1, 'Neville', 'Vyland', 1),
(2, 'Abhishek', 'Shukla', 1),
(3, 'Raghu', 'Nandan', 2);
--
-- Create the report
--
;
with cte_RankData
as
(
select
ROW_NUMBER() OVER (ORDER BY E.CharityName ASC, VoteCount Desc) as RowNum,
RANK() OVER (ORDER BY E.CharityName ASC) AS RankNum,
E.CharityName as CharityName,
C.FirstName + ' ' + C.LastName as FullName,
E.VoteCount
from #tbl_Entry E inner join #tbl_Contestants C on E.ID = C.ID
),
cte_SumData
as
(
select
E.CharityName,
sum(E.VoteCount) as TotalCount
from #tbl_Entry E
group by E.CharityName
)
select
case when RowNum = RankNum then
R.CharityName
else
''
end as rpt_CharityName,
case when RowNum = RankNum then
str(S.TotalCount, 5, 0)
else
''
end as rpt_TotalVotes,
FullName as rpt_ContestantName,
VoteCount as rpt_Votes4Contestant
from cte_RankData R join cte_SumData S
on R.CharityName = S.CharityName

SQL Query to update a column based on the values of other columns in the same table

Ok this is difficult to phrase, so here goes...
I am using MS SQL Server 2008 R2. I have a temp table that lets say has two already populated columns. There is a third empty column I want to populate based on the value of the first two columns. What I want to do is create a guid (using NEWUID()) for each matching combo of col1 and col2. Here is a visual example:
Lets say I have a temp table that looks like this initially:
Name Activity SpecialId
James Running
James Running
James Walking
John Running
John Running
John Walking
I want it to get updated with new GUIDs so that it looks like this:
Name Activity SpecialId
James Running SOMEFAKEGUID_1
James Running SOMEFAKEGUID_1
James Walking SOMEFAKEGUID_2
John Running SOMEFAKEGUID_3
John Running SOMEFAKEGUID_3
John Walking SOMEFAKEGUID_4
Notice how a new GUID is created for each matching pair. So the James/Running combo has the same GUID for all James/Running combos... and the John/Running also has the same GUID for the John/Running combos, but not the same GUID as the James/Running combos do.
I tried to make that as clear as possible, but hopefully that isn't clear as mud!
Can someone show me what the SQL query would look like in order to update that temp table with the correct GUIDs?
Thanks in advance.
Ryan
Using NEWID() seems to be a pain. Using it a CTE creates a sperate ID, so you need some intermediary table instead.
Declare #Table as table (name varchar(20), activity varchar(20) , SpecialID uniqueidentifier)
Declare #DistinctTable as table (name varchar(20), activity varchar(20) , SpecialID uniqueidentifier)
INSERT INTO #Table
(name, activity)
values
('James','Running'),
('James','Running'),
('James','Walking'),
('John','Running'),
('John','Running'),
('John','Walking')
WITH distinctt
AS (SELECT DISTINCT name,
activity
FROM #Table)
INSERT INTO #DistinctTable
SELECT name,
activity,
Newid()
FROM distinctt
UPDATE #Table
SET specialid = dt.specialid
FROM #Table t
INNER JOIN #DistinctTable dt
ON t.activity = dt.activity
AND t.name = dt.name
SELECT * FROM #Table
Produces
name activity SpecialID
-------------------- -------------------- ------------------------------------
James Running AAA22BC5-51FE-43B3-8CC9-4C4A5B4CC981
James Running AAA22BC5-51FE-43B3-8CC9-4C4A5B4CC981
James Walking 1722B76B-5F17-4931-8D7C-2ECADB5A4DFD
John Running FBC1F86B-592D-4D30-ACB3-80DA26B00900
John Running FBC1F86B-592D-4D30-ACB3-80DA26B00900
John Walking 84282844-AAFD-45CA-9218-F7933E5102C6
I'm sure there are better ways to do this, but you can try the following:
WITH TableId AS
(
SELECT DISTINCT Name, Activity
FROM YourTable
)
UPDATE A
SET A.SpecialId = B.SpecialId
FROM YourTable A
INNER JOIN (SELECT Name, Activity, NEWID() SpecialId FROM TableId) B
ON A.Name = B.Name AND A.Activity = B.Activity
Well, I know that you're not using mySQL, but this is how it would work in mySQL (tested)
update temp_table, (
select uuid() as spec_key, name, activity from (
select distinct name, activity from temp_table) as anotherTemp) as anotheranotherTemp
set specialID = anotheranotherTemp.spec_key
where temp_table.Activity = anotheranotherTemp.activity
and temp_table.Name = anotheranotherTemp.name;
It LOOKS like this would work in SQL 2008 (not-tested)
MERGE INTO temp_table AS tt
USING (
select newId() as spec_key, name, activity from (
select distinct name, activity from temp_table) as anotherTemp
ON anotherTemp.activity = tt.activity
and anotherTemp.name = tt.name
WHEN MATCHED
THEN UPDATE
SET specialID = anotherTemp.spec_key;
Performance would not be good though.
I agree with #ConradFrix to use NEWID() first, Lamak script should be modified as mentioned below. It's working fine for me. Thanks for all.
WITH TableId AS
(
SELECT DISTINCT Name, Activity, NEWID() SpecialId
FROM YourTable
)
UPDATE A
SET A.SpecialId = B.SpecialId
FROM YourTable A
LEFT JOIN (SELECT Name, Activity, SpecialId FROM TableId) B
ON A.Name = B.Name AND A.Activity = B.Activity

Sql returns Column has "FristName LastName" as 1 string how to order by Lastname?

I'm using Oracle. My sql returns a column like this
Name:
John Smith
David Lee
...
If I do Order by Name, it will order by first name. How do I order by Last name? If I do Order by Lastname, Firstname oracle returns invalid identifiers. I tried substr, instr but it doesn't work. I know the sql is tedious but just want the data to quickly fix this issue.
Full SQL:
http://pastebin.com/hYkdHBDM
You say your SQL "returns a column" in that format. Do you mean the column is stored that way, or that it's stored as two fields and composed into one in the SQL statement?
If stored that way it's difficult to create an algorithm that will reliably determine what part of a multi-part name is the last name part (indeed, this is sometimes down to personal preference of the person owning the name).
If stored in two separate fields you should be able to ORDER BY LastName, FirstName depending on how the SQL is constructed and whether there are any intermediate views between you and the table. Please post the SQL and table structure.
First, in order to sort by LastName, it needs to be one of the columns you return in each of the queries in your Union All. Second, you can greatly simplify your query by using a common-table expression. Third, do not use the comma delimited syntax for Joins (e.g. From TableA, TableB, TableC...). Instead use the ISO Join syntax.
With RootQuery As
(
Select MeetingID
, FirstName || ' ' || LastName AS Name
, LastName
, CASE WHEN RSVP = 1 THEN 1 ELSE NULL END AS Yes
, CASE WHEN RSVP = 0 THEN 1 ELSE NULL END AS No
, CASE WHEN RSVP = 2 THEN 1 ELSE NULL END AS Phone
, CASE WHEN RSVP = -1 THEN 1 ELSE NULL END AS No_Reply
, MysteryTable0.Response1
, MysteryTable1.Response2
, Note
, groupname
From Attendance A
Join Allusers As B
And B.MemberId = A.PersonId
Join MembershipGroups As M
And M.MemberId = B.MemberID
Join (
SELECT TD.MEMBERID AS MEM0
, Response AS Response1
FROM TRACKINGDETAILS TD, ALLUSERS U
Where TD.MEMBERID = U.MEMBERID
And TD.TRACKINGID = 64
) MysteryTable0
On MysteryTable0.Mem0 = B.MemberId
Join (
SELECT TD.MEMBERID AS MEM1
, Response AS Response2
FROM TRACKINGDETAILS TD, ALLUSERS U
Where TD.MEMBERID = U.MEMBERID
And TD.TRACKINGID = 65
) As MysteryTable1
On MysteryTable1.Mem1 = B.MemberId
Where Meetingid = :1
)
Select MeetingId, Name, LastName, Yes, No, Phone, No_Reply
, Response1, Response2
, Note, GroupName
From RootQuery
Union All
Select Null, 'Total', LastName, SUM(Yes), SUM(No), SUM(Phone), SUM(No_Reply)
, TO_CHAR(SUM(Response1))
, TO_CHAR(SUM(Response2))
, NULL, Groupname
From RootQuery
Group By GroupName
Union All
Select Null, 'Grand Total', LastName, SUM(Yes), SUM(No), SUM(Phone), SUM(No_Reply)
, TO_CHAR(SUM(Response1))
, TO_CHAR(SUM(Response2))
,NULL, ' '
From RootQuery
Group By ???
Order By GroupName Desc, LastName Asc, Name Asc
Btw, the last query will probably have a problem in that it did not have a Group By (which I denoted with Group By ???) but you are using aggregate functions.
What Matthew PK said is correct however he failed to mention that INSTR can parse backwards in which case, his "fail" scenario would be resolved.
Here try this:
create table test_name (f_name varchar2(20), l_name varchar2(20), full_name varchar2(20));
insert into test_name (f_name, l_name, full_name) values ('John', 'Mellencamp', 'John 2Mellen');
insert into test_name (f_name, l_name, full_name) values ('John', 'Mellencamp', 'John C. 1Mellen');
select f_name, l_name, substr(full_name,instr(full_name,' ',-1,1)) as substr, full_name from test_name order by substr(full_name,instr(full_name,' ',-1,1));
Basically the money shot is: substr(full_name,instr(full_name,' ',-1,1))
If you know the field will always be "FirstName LastName" separated by a space you could:
ORDER BY RIGHT(Name, INSTR(Name,' '))
This is the number of characters, on the right side, starting at the space.
This will fail if any other names are separated by a space like "John Cougar Mellencamp"

SQL how to find rows which have highest value of specific column

For example, the table has columns MYINDEX and NAME.
MYINDEX | NAME
=================
1 | BOB
2 | BOB
3 | CHARLES
Ho do I find row with highest MYINDEX for specific NAME? E.g. I want to find ROW-2 for name "BOB".
SELECT Max(MYINDEX) FROM table WHERE NAME = [insertNameHere]
EDIT: to get the whole row:
Select * //never do this really
From Table
Where MYINDEX = (Select Max(MYINDEX) From Table Where Name = [InsertNameHere]
There are several ways to tackle this one. I'm assuming that there may be other columns that you want from the row, otherwise as others have said, simply name, MAX(my_index) ... GROUP BY name will work. Here are a couple of examples:
SELECT
MT.name,
MT.my_index
FROM
(
SELECT
name,
MAX(my_index) AS max_my_index
FROM
My_Table
GROUP BY
name
) SQ
INNER JOIN My_Table MT ON
MT.name = SQ.name AND
MT.my_index = SQ.max_my_index
Another possible solution:
SELECT
MT1.name,
MT1.my_index
FROM
My_Table MT1
WHERE
NOT EXISTS
(
SELECT *
FROM
My_Table MT2
WHERE
MT2.name = MT1.name AND
MT2.my_index > MT1.my_index
)
SELECT MAX(MYINDEX) FROM table
WHERE NAME = 'BOB'
For the whole row, do:
SELECT * FROM table
WHERE NAME = 'BOB'
AND MyIndex = (SELECT Max(MYINDEX) from table WHERE NAME = 'BOB')
If you wanted to see the highest index for name = 'Bob', use:
SELECT MAX(MYINDEX) AS [MaxIndex]
FROM myTable
WHERE Name = 'Bob'
If you want to skip the inner join, you could do:
SELECT * FROM table WHERE NAME = 'BOB' ORDER BY MYINDEX DESC LIMIT 1;
Use
FROM TABLE SELECT MAX(MYINDEX), NAME GROUP BY NAME