How to split a single column into multiple columns in SQL Server select statement? - sql

I am new to SQL Server . I have a single long column with names starting from a, b, c and d. I want to show them in separate columns of NameA, NameB, NameC and NameD. I tried union but it shows in one column. I tried case but I dont know how to use it. Please help.
Existing column
names
A1
B1
A2
C1
A3
A4
A_names| B_names | C_names
A1 | B1 | C1
A2
A3
A4

just for fun and curious why you want that:
select *
from
( select idx = left(names,1)
, names
, rn = row_number() over (partition by left(names,1) order by names)
from
( values ('A1'),('B1'),('A2'),('C1'),('A3'),('A4'),('B2'))
v(names)
) dat
pivot
( max(names)
for idx in ([A],[B],[C],[D])
) p
http://sqlfiddle.com/#!6/9eecb/4013/0

I don't think this is something that should be solved in SQL. It's a representational thing that should probably be done in the application.
However, if you insist to use SQL for this, this is how you could do it. The main problem with this query is that the ROW_NUMBER function will be quite bad for performance.
WITH nameA
(
SELECT name, ROW_NUMBER() OVER(ORDER BY name) AS rn
FROM t1
WHERE name LIKE 'a%'
), nameB AS
(
SELECT name, ROW_NUMBER() OVER(ORDER BY name) AS rn
FROM t1
WHERE name LIKE 'b%'
)
SELECT name FROM nameA
FULL OUTER JOIN nameB
ON nameA.rn = nameB.rn
ORDER BY nameA.rn,nameB.rn;

You can use CASE (https://msdn.microsoft.com/en-us/library/ms181765.aspx)
This Query should work:
SELECT
CASE WHEN users.name like 'a%' THEN users.name ELSE NULL END AS NameA,
CASE WHEN users.name like 'b%' THEN users.name ELSE NULL END AS NameB,
CASE WHEN users.name like 'c%' THEN users.name ELSE NULL END AS NameC,
CASE WHEN users.name like 'd%' THEN users.name ELSE NULL END AS NameD
FROM users

Look at this post, it is very close to your problem.
Itzik Ben-Gan | SQL Server Pro

This isn't really the way relational databases work. When you have data in the same row, it's supposed to be related in some way - a common ID, at the least. What is it that would connect the person whose name happens to begin with A to the person whose name happens to begin with B? Why would you ever want the RDBMS to make such an arbitrary connection?
If you have a requirement to display users in such a way, you'd just want to write several queries and have your presentation layer deal with laying them out properly, e.g.
SELECT name FROM users WHERE name LIKE 'a%'
SELECT name FROM users WHERE name LIKE 'b%'
SELECT name FROM users WHERE name LIKE 'c%'
etc...
The presentation layer could run each query and then populate a table appropriately. Even better would be to have the presentation layer just run this query:
SELECT name FROM users
And then appropriately sort and display the data, which is probably going to be less expensive than multiple scans on your users table by SQL Server.

Related

Query to return nonmatching lines in two arbitrary tables

I have two sets of tables (i.e. a.1, a.2, a.3, b.1, b.2, b.3, etc) created using slightly different logic. The analogous table in the two schemas have the exact same columns (i.e. a.1 has the same columns as b.1). My belief is that the tables in the two schemas should contain the exact same information, but I want to test that belief. Therefore I want to write a query that compares two analogous tables and returns lines that are not in both tables. Is there an easy way to write a query to do that without manually writing the join? In other words, can I have a query that can produce the results that I want where I only have to change the table names I want to compare while leaving the rest of the query unchanged?
To be a bit more explicit, I'm looking to do something like the following:
select *
from a.1
where (all columns in a.1) not in (select * from b.1);
If I could write something like this then all I would have to do to compare a.2 to b.2 would be to change the table names. However, it's not clear to me how to come up with the (all columns in a.1) piece in a general way.
Based on a recommendation in the comments, I've created the following showing the kind of thing I'd like to see:
https://dbfiddle.uk/?rdbms=db2_11.1&fiddle=ad0141b0daf8f8f92e6e3fa8d57e67ad
I was looking for the except clause.
So
select *
from a.1
where (all columns in a.1) not in (select * from b.1);
can be written as
select * from a.1
except
select * from b.1
In db-fiddle I give an explicit exmaple of what I wanted.
If you have a primary key to match rows between the tables, then you can try a full anti-join. For example:
select a.id as aid, b.id as bid
from a
full join b on b.id = a.id
where a.id is null or b.id is null
If the tables are:
A: 1, 2, 3
B: 1, 2, 4
The result is:
AID BID
---- ----
null 4 -- means it's present in B, but not in A
3 null -- means it's present in A, but not in B
See running example at db<>fiddle.
Of course, if your tables do not have a primary key, or if the rows are inconsistent (same PK, different data), then you'll need to adjust the query.
As an alternative you can try this:
select 'a1' t,* from (
select a1.*,row_number() over (partition by c1 order by 1) as rn from a1
minus
select b1.*,row_number() over (partition by c1 order by 1) as rn from b1
)
union all
select 'b1' t,* from (
select b1.*,row_number() over (partition by c1 order by 1) as rn from b1
minus
select a1.*,row_number() over (partition by c1 order by 1) as rn from a1
)
fiddle
edit: you can shorten the query by precalculating the rn part, instead of doing the same calculation again.

I need a query for children that start with my name but doesn't start with any in that set

Suppose I have a table with text primary key called "name". Given a name (that may contain any arbitrary characters including %), I need all of the rows from that table that start with that name, are longer than that name, and that don't start with anything else in the table that is longer than the given name.
For example, suppose my table contains names ad, add, adder, and adage. If I query for "children of ad", I want to get back add, adage. (adder is a child of add). Can this be done efficiently, as I have several million rows? Recursive queries are certainly available.
I have a different approach at present where I maintain a "parent" column. The code to maintain this column is quite painful, and it would be unnecessary if this other approach were reasonable.
I can't tell about its efficiency but I think it works:
with cte as (
select name
from tablename
where name like 'ad' || '_%'
)
select c.name
from cte c
where not exists (
select 1 from cte
where c.name like name || '_%'
);
See the demo.
Equivalent to the above query with a self LEFT JOIN:
with cte as (
select name
from tablename
where name like 'ad' || '_%'
)
select c.name
from cte c left join cte cc
on c.name like cc.name || '_%'
where cc.name is null
See the demo.
Results:
| name |
| ----- |
| add |
| adage |
You could use left on the column like this:
select *
from sometable
where lower(left(name,2)) = 'ad' and length(name) > 2

SQL: How to return records with more than 1 row of data

I'm stuck on a problem where I am creating a report and need to show records which have two or more bank accounts (some of our employees are international and get paid in more than one currency).
The report I created brings back all employees and their bank account information. However, I want this report only to bring back employees with 2 or more bank accounts.
Here is some test data below:
As you can see, Gareth has more than one bank account - what filter can I write to just bring back his record?
Assuming it is mysql, sqlite and even postgresql (I think). it should be
SELECT * FROM table_name WHERE "First Name" = "Gareth";
However if you are using the MySQL binding for excel be sure that "Gareth" is listed for each account he owns. It may look cleaner but SQL will interpret the row beneath "Gareth" as "". Doing this will also mean you will need a new index, "First Name" can't be set as "UNIQUE". Also having spaces in columns is pretty ugly SQL syntax best to use camelCase or under_scores.
Assuming Person_Number as the primary key of the table accounts
SELECT * FROM accounts WHERE accounts.First_Name in
(SELECT a.First_Name
FROM accounts a
INNER JOIN accounts b
ON a.Person_Number = b.Person_Number
WHERE a.First_Name = b.First_Name
AND a.Bank_Account_Name <> b.Bank_Account_Name
);
Try this if you are using database like oracle, sqlserver....
select b1.* from account_info_table b1 where exists (select 1 from account_info_table b2 where b1.first_name=b2.first_name group by b2.first_name having count(*)>2);
or
select * from account_info_table where first_name in (select first_name from account_info_table group by first_name having count(*)>2);
Use This Code :
;WITH _CTE AS
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY FirstName Order By FirstName)ROWNO FROM #Temp
)SELECT FirstName,BankAccountName,BankBranchName,BankAccountNo,SortCode FROM _CTE WHERE ROWNO > 1
select empleado from empleados
group by empleado
having count(*) > 1

Relational algebra without aggregate function

We have a table Phone whose values are
(Name, Number)
--------------
(John, 123)
(John, 456)
(Bravo, 789)
(Ken, 741)
(Ken, 589)
If the question is to Find the guy who uses only one number, the answer is Bravo.
I solved this using aggregate function. But I don't know how to solve without using aggregate function.
Here is my solution:
SELECT *
FROM test t
WHERE NOT EXISTS (
SELECT 1
FROM test
WHERE NAME = t.NAME
AND number <> t.number);
And a sample SQLFiddle.
I'm not sure about this representation in relational algebra (and it's most likely not correct or complete but it might give you a starting point):
RESULT = {(name, number) ∈ TEST | (name, number_2) ¬∃ TEST, number <> number_2}
(this is the main idea, you could probably try and have a look here to try and rewrite this correctly, since I haven't written anything in relational algebra for more than 10 years).
Or maybe you're looking for a different type of representation, like this one here?
You can use LEFT JOIN and use the same table in your JOIN , something like this..
SELECT a.NAME, a.NUMBER FROM test a
LEFT JOIN test b ON a.name = b.name AND a.number <> b.number
WHERE b.name IS NULL;
Hope this helps. :)
Try the following if you are using Access-SQL -
SELECT Name
FROM Phone
GROUP BY Name
HAVING COUNT(Name) = 1;
Otherwise, try -
SELECT Name
FROM Phone
WHERE COUNT(Name) = 1;
If you have any questions, then please feel free to reply.
You can take advantage of RANK() functions something like below.
SELECT * FROM #Tbl WHERE Name NOT IN(
SELECT Name FROM (
SELECT Name, RANK() OVER(PARTITION BY Name ORDER BY Id) AS Rank
FROM #Tbl) t
WHERE t.Rank > 1)
The only draw back of this method is, you need to use unique id from your table to get the right result.
SQLFiddle
Another way of doing it is:
select Name from Phone p
where (select name from Phone p2 where p.name = p2.name and p2.number <> p.number limit 1) is null
Edit: added limit 1 to make sure sub select returns a scalar

(TRANSACT SQL) how to create Master-Detail in a row using sql?

I have two tables
table1
------
ID
NAME
ADDRESS
table2
-------
ID
PHONE
EMAIL
how can i create report like this
------------------------------------
01 Dave 123 Veneu
555-5 A#YAHOO.COM
66-66 B#Yahoo.co.id
213-1 D#c.com
02 John 23 Park
322-1 C#you.com
54-23 D#Net.com
231-2 me#you.com
im using sql server 2005 express,, thank you in advance.
Not sure why you would ever want to write this in anything other than a report designer, but just for the hell of it:
SELECT ID AS Column1, NAME AS Column2, Address AS Column3, ID AS SortColumn1, 1 AS SortColumn2
UNION
SELECT '', PHONE, EMAIL, ID AS SortColumn1, 2 AS SortColumn2
ORDER BY SortColumn1, SortColumn2
The output is going to basically be a load of gibberish really, and you've got the two extra columns on the end of to get rid of.
You shouldn't. It's a general principle that formatting should not be done in the database layer.
SQL Server should be used to generate data, then your application should process the data, including the formatting.
I would open two queries. One that loads table one, ordered by the ID column. And the other that load table two, also ordered by the ID column. You can then iterate through both record sets at the same time, something like the following pseudo-code...
rs1 = SQL.Execute("SELECT * FROM table1 ORDER BY ID")
rs2 = SQL.Execute("SELECT * FROM table2 ORDER BY ID, phone")
rs2.Next()
WHILE rs1.Next()
Output The Address Info Here
WHILE rs1.ID = rs2.ID
Output The Phone/Email Info Here
rs2.Next()
END WHILE
END WHILE
In order to have one-to-many relationship, as in your example (one person has multiple phones and emails), you need to add some kind of link column to the second table, which would contain the ID of the person the email / phone belongs to.
So your table structure should look like this:
table1
------
ID
NAME
ADDRESS
table2
-------
ID
TABLE1_ID
PHONE
EMAIL
Then, you could query your data using joins:
SELECT table1.name, table1.address, table2.phone, table2.email WHERE table2.table1_id = table1.id
I strongly agree with dems, but if you really need to come up with something like that, the following could work (albeit without the empty lines)
SELECT case
when group_rn = 1 then id
else ''
end as id,
case
when group_rn = 1 then name
else phone
end as name_phone_column,
case
when group_rn = 1 then address
else _email
end as address_email_column
FROM (
SELECT t1.id,
t1.name,
t1.address,
t2.email,
t2.phone,
row_number() over (partition by t1.id order t1.name) as group_rn
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.id
) t
ORDER BY id
This assumes that phone and name both have the same datatype, just like address and email.