How to combine columns from 2 tables into 1 without using JOIN - sql

I am using SQL Server 2008. I have 2 table variables like
FirstName
==========
Little
John
Baby
LastName
==========
Timmy
Doe
Jessica
And I want the result table be:
First Last
=====================
Little Timmy
John Doe
Baby Jessica
Note that there is no PK can join the 2 tables. I am trying to use a cursor but not sure how to start.
---- Updated -----
I know it's a rare case, but I am writing a script to clean up legacy data. The only way we know "Little" goes with "Timmy" is that they are both the first record of the table. Would it help if we had PK for the tables but there is no relation?
ID FirstName
==========
1 Little
2 John
3 Baby
----------
ID LastName
==========
4 Timmy
5 Doe
6 Jessica
----------
I am not familiar with TSQL so I thought I can loop through the 2 tables like looping through Arrays in memory.

You could try something like this, to match up based on row numbers:
SELECT FirstName AS First, LastName AS Last
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, FirstName
FROM FirstName
) t1
INNER JOIN
(
SELECT ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, LastName
FROM LastName
) t2
ON t1.RowNum = t2.RowNum
But don't take this as a signal that you don't need keys.

You can't reliably join these two table variables without identities. Assuming they are being populated in an ordinal manner in the first place, each table could be created with identities as follows:
DECLARE #first TABLE(ID INT IDENTITY(1,1), NameFirst VARCHAR(30));
DECLARE #last TABLE(ID INT IDENTITY(1,1), NameLast VARCHAR(30));
-- Note that we don't need to list column names here
INSERT INTO #first VALUES('Little');
INSERT INTO #first VALUES('John');
INSERT INTO #first VALUES('Baby');
INSERT INTO #last VALUES('Timmy');
INSERT INTO #last VALUES('Doe');
INSERT INTO #last VALUES('Jessica');
SELECT n1.NameFirst
, n2.NameLast
FROM #first n1
INNER JOIN #last n2 ON n1.ID=n2.ID;
Result:
NameFirst NameLast
------------------------------ ------------------------------
Little Timmy
John Doe
Baby Jessica

There is no such thing as a "first record". Reading order is totally undefined. This holds true in practice as well! You will see random row order in practice.
You need to define a sorting order. After doing that you can query like this:
SELECT FirstName AS First, LastName AS Last
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY ID) AS r, FirstName
FROM FirstName
) t1
INNER JOIN
(
SELECT ROW_NUMBER() OVER (ORDER BY ID) AS r, LastName
FROM LastName
) t2
ON t1.r = t2.r
Notice the placeholder "SORT ORDER HERE". Need to fill that in. Example: "ID" or "FirstName".
Edit: Having seen your edit I added sort order on ID. This will work now.

Here is a hack, that might work in your case:
select t1.id, t1.name, t2.name
from (select name, row_number() over (partition by null order by (select NULL)) as id
from t1
) t1 join
(select name, row_number() over (partition by null order by (select NULL)) as id
from t2
) t2
on t1.id = t2.id
In my experience, the row_number() with the subquery for the order by means that no sort is actually done, but the rows are brought in sequentially. This is more likely to work if you are using a single threaded instance.
And, I don't guarantee that it will work! Definitely check the results! If this doesn't work, you might be able to get the same effect by using temporary tables.
Is this data coming from outside the database world? If you have to perform this process more than once, then you should handle the problem either externally or on input into the database.

Building on solution #JohnDewey presented, you can sort the records into the table variables. In practice, it will create a relation between the values based on the order of the records:
DECLARE #first TABLE(sequence INT IDENTITY(1,1), FirstName VARCHAR(30));
DECLARE #last TABLE(sequence INT IDENTITY(1,1), LastName VARCHAR(30));
INSERT INTO #first(FirstName)
SELECT FirstName FROM TableFisrt ORDER BY id;
-- sequence FirstName
-- ======== =========
-- 1 Little
-- 2 John
-- 3 Baby
INSERT INTO #last(LastName)
SELECT FirstName FROM TableLast ORDER BY id;
-- sequence LastName
-- ======== =========
-- 1 Timmy
-- 2 Doe
-- 3 Jessica
SELECT frs.FirstName, lst.LastName
FROM #first frs
INNER JOIN #last lst ON frs.sequence = lst.sequence;
-- sequence FirstName
-- ======== =========
-- Little Timmy
-- John Doe
-- Baby Jessica

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 View Assign Alternating Data from Another Table

I cannot explain this thoroughly with words as English is not my native language so I will try visual presentation. First, I have a table that looks like this let's call it tblPerson:
tblPerson
ID Name
1 John
2 Paul
3 James
Then I have another table tblPhones:
tblPhones
ID Mobile
1 123456
2 654321
Now for my question, is it possible to create a view that will look like this:
Person-Phone
Name Mobile
John 123456
Paul 654321
James 123456
What I want to display is a list of person, and use the tblPhones to assign the mobile column but assign it alternately. So if a new person is added lets say Mark. The view would look like this:
Person-Phone
Name Mobile
John 123456
Paul 654321
James 123456
Mark 654321
How can I query this?
Try this:
SELECT Name, Mobile
FROM (
SELECT Name,
ROW_NUMBER() OVER (ORDER BY ID) As rn
FROM tblPerson) AS t1
INNER JOIN (
SELECT Mobile,
ROW_NUMBER() OVER (ORDER BY ID) AS rn,
COUNT(*) OVER () AS cnt
FROM tblPhones
) AS t2 ON (t1.rn - 1) % cnt + 1 = t2.rn
Demo here
#Giorgos beat me to it, but here's my version. You don't need the row_number window function assuming the IDs are contiguous (if they're not, you do :).
CREATE TABLE #tblPerson (ID INT,Name VARCHAR(5));
CREATE TABLE #tblPhones (ID INT, Mobile VARCHAR(6));
INSERT INTO #tblPerson(ID, Name) VALUES( 1, 'John'),( 2, 'Paul'),( 3, 'James');
INSERT INTO #tblPhones(ID, Mobile) VALUES( 1,'123456'),( 2,'654321');
SELECT
Name, Mobile
FROM #tblPerson
JOIN #tblPhones ON #tblPhones.ID = ((#tblPerson.ID-1) % (SELECT COUNT(*) FROM #tblPhones) +1)
ORDER BY #tblPerson.ID

One SQL statement for counting the records in the master table based on matching records in the detail table?

I have the following master table called Master and sample data
ID---------------Date
1 2014-09-07
2 2014-09-07
3 2014-09-08
The following details table called Details
masterId-------------Name
1 John Walsh
1 John Jones
2 John Carney
1 Peter Lewis
3 John Wilson
Now I want to find out the count of Master records (grouped on the Date column) whose corresponding details record with Name having the value "John".
I cannot figure how to write a single SQL statement for this job.
**Please note that join is needed in order to find master records for count. However, such join creates duplicate master records for count. I need to remove such duplicate records from being counted when grouping on the Date column in the Master table.
The correct results should be:
count: grouped on Date column
2 2014-09-07
1 2014-09-08
**
Thanks and regards!
This answer assumes the following
The Name field is always FirstName LastName
You are looking once and only once for the John firstname. The search criteria would be different, pending what you need
SELECT Date, Count(*)
FROM tblmaster
INNER JOIN tbldetails ON tblmaster.ID=tbldetails.masterId
WHERE NAME LIKE 'John%'
GROUP BY Date, tbldetails.masterId
What we're doing here is using a wilcard character in our string search to say "Look for John where any characters of any length follows".
Also, here is a way to create table variables based on what we're working with
DECLARE #tblmaster as table(
ID int,
[date] datetime
)
DECLARE #tbldetails as table(
masterID int,
name varchar(50)
)
INSERT INTO #tblmaster (ID,[date])
VALUES
(1,'2014-09-07'),(2,'2014-09-07'),(3,'2014-09-08')
INSERT INTO #tbldetails(masterID, name) VALUES
(1,'John Walsh'),
(1,'John Jones'),
(2,'John Carney'),
(1,'Peter Lewis'),
(3,'John Wilson')
Based on all comments below, this SQL statement in it's clunky glory should do the trick.
SELECT date,count(t1.ID) FROM #tblmaster mainTable INNER JOIN
(
SELECT ID, COUNT(*) as countOfAll
FROM #tblmaster t1
INNER JOIN #tbldetails t2 ON t1.ID=t2.masterId
WHERE NAME LIKE 'John%'
GROUP BY id)
as t1 on t1.ID = mainTable.id
GROUP BY mainTable.date
Is this what you want?
select date, count(distinct m.id)
from master m join
details d
on d.masterid = m.id
where name like '%John%'
group by date;

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