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

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

Related

PostgreSQL subquery using like

lets say i have first table like this
Branch table
|name |description|
|123456ABC|FOO |
|553646DEF|FO2 |
and second table like this
Balance table
|name|description|
|ABC |oof |
|DEF |2of |
i want to query to Balance table, where each row containing name from Branch table..
for example "123456ABC" in Branch table, i want to get "ABC" row from Balance table
how could i achieve that? i've tried this query so far with no luck
select * from Balance
where name like (
SELECT `name` FROM Branch
);
any suggestion?
You should convert the balance's names to LIKE patterns:
SELECT * FROM Balance
WHERE (
SELECT `name` FROM Branch
) LIKE '%' || name;
A join may look more readable:
SELECT b.* FROM Balance b JOIN Branch r ON r.name LIKE '%' || b.name;
I don't know if you will have dupes or not, so you may want to consider using a semi-join. For large datasets, a semi-join will typically be more efficient than an in-list query.
#clemens solution looks good, assuming no dupes. Alternatively, you can use regex:
select *
from balance ba
where exists (
select null
from branch br
where
br.name ~ ba.name
)
Performance-wise, I think like will outperform the regex, but it's an option.
Also, if your string is always at the end, you can consider a join using right or substr:
select *
from balance ba
where exists (
select null
from branch br
where
right (br.name, length (ba.name)) = ba.name
)

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

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.

How to write a SQL-Query for the following table?

My table is defined like this:
Name is a string and property too.
ID | Name | Property
An example for data in this table is this:
ID | Name | Property
1 Peter Newsletter
2 Paul Register
3 Peter Register
4 Shaun Newsletter
5 Steve Register
Now I like to query all people that have the property newsletter and register.
As a result I should get Peter, because he has both property's.
So the resulting table should be like:
ID | Name | Property
1 Peter Newsletter
3 Peter Register
So everything I try to query is which person has both property's newsletter and register.
Here is one method:
select t.*
from table t
where exists (select 1
from table t2
where t2.name = t.name and t2.property = 'NewsLetter'
) and
exists (select 1
from table t2
where t2.name = t.name and t2.property = 'Register'
);
If you just want the list of names, perhaps with ids, I would do that as:
select t.name
from table t
where t2.property in ('NewsLetter', 'Register')
group by t.name
having count(distinct property) = 2;
How you get the list of ids depends on your database, something like listagg() or group_concat() or string_agg().
An alternative, pretty much on the same lines as Gordon's solution, but without using EXISTS:
select * from tablename
where name in (select name from tablename where property = 'Newsletter')
and name in (select name from tablename where property = 'Register')
One more way:
SELECT * FROM T as T1
WHERE Property IN ('Newsletter','Register')
AND EXISTS (SELECT * FROM T
WHERE Name=T1.Name
and Property IN ('Newsletter','Register')
and Property <> T1.Property
)
SQLFiddle demo
Another one, for the record
WITH cteHasBoth
as (select Name
from MyTable
where Property in ('Newsletter', 'Register')
group by Name
having count(*) = 2)
select ID, Name
from MyTable
where name in (select Name from cteHasBoth)
This would require only two sacns through the table.
It's hard to be sure without knowing more about the data. Given the exact requirements that you gave to us, this will give the results you showed:
WITH multprop (multName) AS (
SELECT NAME FROM myTable
WHERE Property IN('Newsletter','Register')
GROUP BY NAME
HAVING count(*)>1 )
select id, Name, Property
from multprop inner join myTable
on multName = Name
But minor differences in your requirements will mess things up. For example, will there ever be Property values other than the two you listed? Or can a Name show up multiple times with the same Property?
EDIT: The added WHERE clause limits rows in the CTE to the requested specific set of Property values. This is from the more detailed requirements in comments.

SQL Grouping by Like using select statement

I have a table with unique names and a combination of those same names separated by commas in the same field as below:
Bill
Mark
Steve
Bill, Mark
Mark, Steve
Bill,Mark, Steve
I would like to Group the names not separated by a comma for a count of those where the name exists such as:
Bill 3
Mark 4
Steve 3
In the future someone may add another name to the table so I can't use a Case statement with static names. I would like something like this:
SELECT
Name
FROM
My_Table
Group By
Name Like (SELECT Name FROM My_Table Where Name Not Like '%,%')
Is that possible?
Select N.Name, COUNT(*)
FROM (
SELECT Name
FROM My_Table
WHERE Name NOT LIKE '%,%'
) Names N
JOIN My_Table MT
ON (MT.Name LIKE '%' + N.Name + ',%' OR MT.Name LIKE '%,' + N.Name + '%' OR MT.Name = N.Name)
GROUP BY N.Name
I don't have an instance of SQLServer to test this out, but an approach that may work is selecting only the simple-name records and then a nested SELECT expression that will count all records with that name. Something like this:
SELECT
Name,
(SELECT COUNT(*) FROM YourTable Y2 WHERE Name LIKE ('%' + Y1.Name + '%'))
FROM YourTable Y1 WHERE NAME NOT LIKE '%,%'
It will fail, of course, on nested names. (Bob and Bobby, if they're different people, for instance). A more robust approach would require removing all the spaces from around commas and building out the LIKE expression into three LIKEs ORed together. If you can't create a LIKE value in-line the way I did, you can substitute whatever the SQLServer function for location-within-string is.
But, honestly, I'd project a temporary normalized table and base your report off that.

SQL get rows matching ALL conditions

I would like to retrieve all rows matching a set of conditions on the same column. But I would like the rows only if ALL the conditions are good, and no row if only one condition fails.
For example, taking this table:
|id|name|
---------
|1 |toto|
|2 |tata|
I would like to be able to request if "tata" && "toto" are in this table. But when asking if "tata" and "tuto" are in, I would like an empty response if one of argument is in not in the table, for example asking if "toto" && "tutu" are included in the table.
How can I do that ?
Currently, I'am doing one query per argument, which is not very efficient. I tried several solutions including a subselect or a group+having, but no one is working like I want.
thanks for your support !
cheers
This isn't the most efficient way, but this query would work.
SELECT * FROM table_name
WHERE (name = 'toto' OR name = 'tata')
AND ( SELECT COUNT(*) FROM table_name WHERE name = 'toto') > 0
AND ( SELECT COUNT(*) FROM table_name WHERE name = 'tata') > 0
This is a little vague. If the names are unique, you could count the matching rows that match a where clause:
where name='toto' or name='tata'
If the count is 2, then you know both matched. If name is not unique you could potentially select the first ID (select top 1 id ...) that matches each in a union and count those with an outer select.
Even if you had an arbitrary number of names to match, you could create a stored procedure or code in whatever top-level language you are using to build the select statement.
SELECT 1 AS found FROM hehe
WHERE 1 IN (SELECT 1 FROM hehe WHERE name='tata')
AND 1 IN (SELECT 1 FROM hehe WHERE name='toto')
If name is unique you can simplify to:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND (SELECT count(*) FROM tbl WHERE name IN ('toto', 'tata')) > 1;
If it isn't:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND EXISTS (SELECT * FROM tbl WHERE name = 'toto')
AND EXISTS (SELECT * FROM tbl WHERE name = 'tata');
Or, in PostgreSQL, MySQL and possibly others:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND (SELECT count(DISTINCT name) FROM tbl WHERE name IN ('toto', 'tata')) > 1;