Get all results in a single row - sql

When I take a 'SELECT *' query from a table I would like all the results to be displayed in a single 'string' format.
For example:
SELECT * FROM Employees;
Returns:
Id FirstName LastName Age
1 John Smith 30
Instead I would like to get:
Id=1,FirstName=John,LastName=Smith,Age=30
But, if I do exactly the same for the query: SELECT * FROM Cars;
I want this query to adapt and just dynamically gets the columns from the table 'Cars' and do the same with it.

Does one of this select meets your requirements ?
Declare #Employees table(Id integer, FirstName varchar(100), LastName varchar(100), Age integer)
insert into #Employees values (1, 'John', 'Smith', 30), (2, 'John', 'Doe', 23)
select CONCAT('Id=', Id, ', FirstName=', FirstName, ', LastName=', LastName, ', Age=', Age) as Employees from #Employees
select
STUFF ((
select CONCAT('; ', 'Id=', Id, ', FirstName=', FirstName, ', LastName=', LastName, ', Age=', Age) from #Employees
FOR XML PATH('')),1,2, '') as employees
OUTPUT :
Employees
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Id=1, FirstName=John, LastName=Smith, Age=30
Id=2, FirstName=John, LastName=Doe, Age=23
Employees
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Id=1, FirstName=John, LastName=Smith, Age=30; Id=2, FirstName=John, LastName=Doe, Age=23
i don't have the Employees table so i made my own in a variable, but don't mind this, you can test this code without modifying the requests

I want to add that it's code I will use in a trigger, I will be using the 'inserted' and 'deleted' tables. This makes it much harder since I'm not able to use them in a stored procedure.

Related

Function to check for NULL and concatenating the strings

I have a SQL statement that concatenates First Name and Last Name of the person. I use the following SQL statement.
ISNULL(FullName.FirstName, '') + ' ' + ISNULL(FullName.LastName, '')
What I need is to change this to add the FirstName and LastName only if they are not both NULL. If one of them is not NULL, use that value.
Examples:
FirstName and LastName are NULL -> NULL
FirstName is John and LastName is NULL -> John
FirstName is NULL and LastName is Doe -> Doe
-> indicates the result
Use CONCAT() to concatenate without any NULL issues
TRIM() to remove any spaces for when one or both fields are missing
NULLIF() to replace '' with NULL if first and last name are both
missing
SQL Server 2017+ Solution
DROP TABLE IF EXISTS #emp
CREATE TABLE #emp (ID INT IDENTITY(1,1), FirstName VARCHAR(25), LastName VARCHAR(25));
INSERT INTO #emp
VALUES(null,null)
,('John',null)
,('Doe',null)
,('John', 'Doe');
SELECT FullName = NULLIF(TRIM(CONCAT(FirstName,' ',LastName)),'')
,*
FROM #emp
Actually if you just wrap you current expression inside TRIM() it should give you the behavior you want:
SELECT TRIM(ISNULL(FullName.FirstName, '') + ' ' +
ISNULL(FullName.LastName, '')) AS FullName
FROM yourTable;
In the event that only the first or last name be present, your original expression would leave a dangling leading/trailing space. The call to TRIM() fixes that by removing this extra space.
You can use simple case statement to solve this:
create table emp (emp_id int4, fname varchar(10), lname varchar(10));
insert into emp (emp_id) values(1);
insert into emp (emp_id,fname) values(2, 'John');
insert into emp (emp_id,lname) values(3, 'Doe');
insert into emp (emp_id,fname,lname) values(4, 'John', 'Doe');
select emp_id,fname, lname,
case
WHEN FNAME is not null and LNAME is not null then
FNAME||' '||LNAME
WHEN FNAME is not null and LNAME is NULL then
FNAME
WHEN FNAME is null and LNAME is not NULL then
LNAME
else null
end as FULL_NAME
from emp;
A better SQL Server 2017 solution:
Use the new CONCAT_WS function, which concatenates with a separator, ignoring nulls
CREATE TABLE #emp (ID INT IDENTITY(1,1), FirstName VARCHAR(25), LastName VARCHAR(25));
INSERT INTO #emp
VALUES(null,null)
,('John',null)
,('Doe',null)
,('John', 'Doe');
SELECT FullName = CONCAT_WS(' ', FirstName, LastName)
,*
FROM #emp;
db<>fiddle

SQL Boolean expression

I'm new to SQL but I thought naively thought this would be simple.
I have a SQLITE table with FIRSTNAME and LASTNAME columns. I want to exclude specific individuals, e.g. 'Fred Smith' and 'Joe Bloggs'. I used the following:
SELECT FIRSTNAME, SURNAME
FROM MyTable
WHERE (FIRSTNAME != 'Fred' AND LASTNAME != 'Smith');
but that excludes all FIRSTNAMEs = 'Fred' and all LASTNAMEs = 'Smith'. How do I achieve what I need? Secondly, how do I achieve that for multiple individuals, i.e. 'Joe Bloggs' as well as 'Fred Smith'?
How do I achieve what I need?
SELECT FIRSTNAME,SURNAME FROM MyTable WHERE NOT (FIRSTNAME = 'Fred' AND LASTNAME = 'Smith');
or the equivalent
SELECT FIRSTNAME,SURNAME FROM MyTable WHERE (FIRSTNAME != 'Fred' OR LASTNAME != 'Smith');
How do I achieve what I need? Secondly, how do I achieve that for multiple individuals, i.e. 'Joe Bloggs' as well as 'Fred Smith'?
Simply add the relevant AND clause.
SELECT FIRSTNAME,SURNAME FROM MyTable WHERE NOT (FIRSTNAME = 'Fred' AND LASTNAME = 'Smith') AND NOT (FIRSTNAME = 'Joe' AND LASTNAME = 'Bloggs');
Use VALUES to return all the names that you want to exclude and the EXCEPT operator to filter them out:
SELECT FIRSTNAME, LASTNAME FROM MyTable
EXCEPT
VALUES ('Fred', 'Smith'), ('Joe', 'Bloggs');
See the demo.

select query to match multiple columns with multiple keywords

I need to write a select query that will search a single table for words taken from a user input query string like "John Doe Engineering". The string can consist of a single or multiple words. The query string will be passed into a stored procedure as a parameter. In total there area about 20 columns that need to be searched. My first thought was something like this:
SELECT *
FROM Employees
WHERE FirstName LIKE '%John%' OR FirstName LIKE '%Doe%' OR FirstName LIKE '%Engineering%'
WHERE LastName LIKE '%John%' OR LastName LIKE '%Doe2%' OR LastName LIKE '%Engineering%'
WHERE Manager LIKE '%John%' OR Manager LIKE '%Doe%'OR Manager LIKE '%Engineering%'
WHERE Department LIKE '%John%' OR Department LIKE '%Doe%'OR Department LIKE '%Engineering%'
--repeat for 16 more table columns
But I'm not sure how to best generate the query syntax based upon the user query string input. Furthermore this seems like it would be a highly inefficient query. Would it be better to look at using full text search in this case? I'm wondering what the best approach might be?
As everyone else said - Full text search is probably the best solution for this type of thing. That said, I thought it would be fun to offer a T-SQL solution.
Quick Disclaimer 1
*I would strongly encourage you not to use the solutions below - this was intended to be a fun little SQL exercise; the performance would be bad. Also - I demonstrate two very efficient ways to split a string: one using Jeff Moden's DelimitedSplit8K, the other technique using PARSENAME *
Quick Disclaimer 2
I should point out a problem with concatenating the columns into a single string as a couple people suggested -- it can lead to false positives; consider the following query:
DECLARE #search varchar(100) = 'ab';
WITH sampleData AS (SELECT fn, ln FROM (VALUES ('aa', 'bb'), ('cc', 'dd')) t(fn,ln))
SELECT *
FROM sampleData
WHERE CONCAT(fn,ln) LIKE '%'+#search+'%';
The above query will return the first record even though the "ab" does not exist in either column. For that reason you would change the WHERE (or CHARINDEX in John's example) to look like this:
WHERE CONCAT(fn, '|||', ln) LIKE '%'+#search+'%';
My Solutions
-- SAMPLE DATA
-------------------------------------------------
DECLARE #employees TABLE
(
FirstName varchar(100),
LastName varchar(100),
Manager varchar(100),
Department varchar(100)
);
INSERT #employees
SELECT *
FROM
(
VALUES
('bob', '****', 'ddd', 'sss'),
('fff', 'fred', 'obx', 'ccc'),
('Sue', 'abcd', 'ddd', 'zzz'),
('ddd', 'dcba', '123', 'fobbb')
) xx(x1, x2, x3, x4);
-- Solution #1: when #search has <= 4 "items"
-------------------------------------------------
DECLARE #search varchar(100) = 'xx bb ff zz';
SELECT e.*
--,PARSENAME(REPLACE(#search,' ','.'), N) AS matchedPattern
FROM (VALUES (1),(2),(3),(4)) t(n)
CROSS JOIN #employees e
WHERE
CHARINDEX
(
PARSENAME(REPLACE(#search,' ','.'), N),
CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department)
) > 0;
-- Solution #2: when #search has (or can have) > 4 "items"
-------------------------------------------------
-- for this you will need delimitedsplit8k: http://www.sqlservercentral.com/articles/Tally+Table/72993/
SELECT e.*
FROM dbo.delimitedsplit8k(#search, ' ')
CROSS JOIN #employees e
WHERE
CHARINDEX
(
item,
CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department)
) > 0;

Inserting multiple names in same cell with separated commas

Previously, I was trying to keep ALL previous last names of an employee in a table cell with commas (see below) but I didn’t know how. Someone then suggested using normalization which I’m not sure if it’ll be easier in this situation. My boss wants to display all previous last names of an employee on a web page each time she edits her account info. Simply put, when Judy changes her last name again – her last name Kingsley should be inserted behind Smith. So, my question is back to whether or not it is possible to add multiple last names in the same cell, separated with commas as I thought it’ll be simpler when I use a variable on the web page to display all the Alias at once? Yet, I’ve no clue the complexity to write the codes for this. Any help is truly appreciated.
Current SQL table
+---------------+-----------------+----------------+--------------------+
People FirstName LastName Alias
+---------------+-----------------+----------------+--------------------+
002112 Judy Smith Hall
Preferred
+---------------+-----------------+----------------+--------------------+
People FirstName LastName Alias
+---------------+-----------------+----------------+--------------------+
002112 Judy Kingsley Hall, Smith
Keep the database normalized.
People:
(Id, Firstname, Lastname)
LastnameHistory:
(PeopleId, OldLastname, NewLastname, DateChanged)
You can the create a view which would be a "GROUP_CONCAT" type of query to transform the data as required.
An example:
DECLARE #people TABLE ( id INT IDENTITY(1,1), fname VARCHAR(50), lname VARCHAR(50))
DECLARE #lnameHistory TABLE ( id INT IDENTITY(1,1), people_id INT, lname VARCHAR(50), date_changed DATETIME)
INSERT INTO #people (fname, lname)
VALUES ('john', 'smith'), ('jane', 'doe')
INSERT INTO #lnameHistory (people_id, lname, date_changed)
VALUES (2, 'shakespeare', '2012-01-01'), (2, 'einstein', '2013-12-12')
;WITH group_concat AS
(
SELECT people_id, LEFT(lnames , LEN(lnames )-1) AS lnames
FROM #lnameHistory AS o
CROSS APPLY
(
SELECT lname + ', '
FROM #lnameHistory AS i
WHERE o.people_id = i.people_id
ORDER BY date_changed ASC
FOR XML PATH('')
) pre_trimmed (lnames)
GROUP BY people_id, lnames
)
SELECT p.*, gc.lnames FROM #people p
JOIN group_concat gc ON gc.people_id = p.id
Some reference for syntax:
SQL Server CROSS APPLY and OUTER APPLY
XML Data (SQL Server)
Assuming your update statement is a stored procedure taking in parameters of #personId and #newLastName:
EDIT
Sorry, wrong version of SQL. Have to do it the old school way!
UPDATE PeopleTable
SET Alias = Alias + ', ' + LastName,
LastName = #newLastName
WHERE
People = #personId
When you update the table for a new LastName, use something like this:
UPDATE <table> SET Alias = Alias + ', ' + LastName, LastName = <newLastName>

Distinct over one column with value comparing on another column ICriteria NHibernate

I have table and object called Person. I have problem to create a distinct (over column "lastname") criteria. I want to get only the oldest Person with distinct lastnames.
For example i have (properties: firstname, lastname, age):
John Smith, 52
Jessica Smith, 45
Ann Pit, 21
Brad Pit, 30
Can anybody help me to create criteria which result i get Person object with John Smith and Brad Pit?
Probably the best approach here is to use EXISTS to filter the result set, first a SQL example to get the logic correct:
DECLARE #Person TABLE (
Id INT,
Firstname VARCHAR(20),
Lastname VARCHAR(20),
Age INT
)
INSERT INTO #Person VALUES (1, 'Brad', 'Pitt', 42)
INSERT INTO #Person VALUES (2, 'Angelina', 'Pitt', 45)
INSERT INTO #Person VALUES (3, 'John', 'Smith', 50)
INSERT INTO #Person VALUES (4, 'Jane', 'Smith', 55)
SELECT P.* FROM #Person P
WHERE EXISTS(
SELECT SUB.LastName, MAX(SUB.Age) as Age FROM #Person SUB
GROUP BY SUB.LastName
HAVING SUB.LastName = P.LastName AND MAX(SUB.Age) = P.Age)
This yields the following results which is as expected:
Id Firstname Lastname Age
-------------------------------
2 Angelina Pitt 45
4 Jane Smith 55
Now to convert to nHibernate, this effectively builds the same query as above:
var subQuery = DetachedCriteria.For<Person>("SUB")
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("LastName"), "Lastname")
.Add(Projections.Max("Age"), "Age"))
.Add(Restrictions.EqProperty(Projections.Max("Age"), "P.Age")
.Add(Restrictions.EqProperty("LastName", "P.LastName"));
return session.CreateCriteria<Person>("P")
.Add(Subqueries.Exists(subQuery))
.List<Person>();