I need to group the same ID into one row and keep the row containing the most data.
If an ID group have no data, I still want 1 row returned. I have roughly 30 data columns.
Example:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
1
City2
Country2
Null
Null
Null
1
City3
Country3
Null
Null
Null
2
City1
Country1
DataA1
DataB1
DataN1
2
City2
Country2
Null
Null
Null
2
City3
Country3
Null
Null
Null
3
City1
Country1
Null
Null
Null
3
City2
Country2
Null
Null
Null
3
City3
Country3
Null
Null
Null
Result:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
2
City1
Country1
DataA1
DataB1
DataN1
3
City1
Country1
Null
Null
Null
Any suggestion would greatly be appreciated!
This is easy to do if you sum the number of non-null columns and then apply a row_number
with cte as (
select *, Row_Number() over(partition by id order by tot desc) rn
from (
select * , Iif(data1 is null,0,1) + Iif(data2 is null,0,1) + Iif(data30 is null,0,1) tot
from t
)x
)
select id, city, country, data1, data2, data30
from cte
where rn=1
See Working demo
Related
I have a database with the following tables :
UserTable :
-----UserID (int) PK,
-----UserName (Varchar) null
PhoneNumber :
------PhoID (int) PK,
------UserID(int) FK to UserTable
------PhoneNumber (Varchar) null
Address
------AddressID (int) PK,
------UserID(int) FK to UserTable
------AddressName (Varchar) null.
The users can have many phone numbers and many addresses. It is a one to many relationship.
I have the following data in these tables.
UserID UserName
1 Bridgerton
2 Merlin
3 Victoria
PhoID UserID PhoneNumber
1 1 phone1
2 1 phone2
3 1 phone3
4 2 Phone21
5 2 9909909900
AddressID UserID AddressName
1 1 Chennai
2 1 Gurgaon
3 2 Hyderabad
4 2 Mumbai
5 2 Gurgaon
Now I need the following result, want to see the userdetails, his phone number and the list of addressess in one go.
I wrote the following query,
select UserName, PhoneNumber, AddressName
from dbo.UserTable a
left outer join dbo.PhoneNumber b
on a.UserID=b.UserID
left outer join dbo.Address c
on a.UserID = c.UserID
Results
UserName PhoneNumber AddressName
Bridgerton phone1 Chennai
Bridgerton phone1 Gurgaon
Bridgerton phone2 Chennai
Bridgerton phone2 Gurgaon
Bridgerton phone3 Chennai
Bridgerton phone3 Gurgaon
Merlin phone21 Hyderabad
Merlin phone21 Mumbai
Merlin phone21 Gurgaon
Merlin 9909909900 Hyderabad
Merlin 9909909900 Mumbai
Merlin 9909909900 Gurgaon
Victoria NULL NULL
I know its messed up but need to see the username, userphone numbers and useraddress for a particular userid like this in just one record.
userid username Phonenumber Address
1 Bridgerton phone1, phone2, phone3 Gurgaon, Hyderabad
One option is to use UNION ALL to normalize your data then then perform a simple conditional aggregation/string_agg()
Example
;with cte as (
Select UserID
,Seq = UserID
,Col = 'UserName'
,Val = UserName
From UserTable
Union All
Select UserID
,Seq = PhoID
,Col = 'Phone'
,Val = PhoneNumber
From PhoneNumber
Union All
Select UserID
,Seq = AddressID
,Col = 'Address'
,Val = [AddressName]
From Address
)
Select UserID
,UserName = max(case when col='UserName' then Val end)
,PhoneNumber = string_agg( case when col='Phone' then Val end,',') within group ( order by seq)
,Address = string_agg( case when col='Address' then Val end,',') within group ( order by seq)
From cte
Group By UserID
Results
UserID UserName PhoneNumber Address
1 Bridgerton phone1,phone2,phone3 Chennai,Gurgaon
2 Merlin Phone21,9909909900 Hyderabad,Mumbai,Gurgaon
3 Victoria NULL NULL
I have a temp table that looks something like this:
Record DepartmentId PositionId EmployeeId StatusId CustomerId
1 Null Null Null 4
2 7 454 Null Null
3 Null 454 Null 3
3 Null Null Null Null 214
3 Null Null Null Null 100
3 Null Null Null Null 312
4 Null Null Null Null 357
I inserted the above into the temp table from tables that looked like this:
Record Table Record-to-Department Record-to-Position
Record Name Record DepartmentId Record PositionId
1 Red 2 7 2 454
2 blue 3 454
3 Green
4 Purple
Record-To-Status Record-To-Customer
Record StatusId Record CustomerId
1 4 3 214
3 3 3 100
3 312
4 357
I have an employee whose record looks something like this:
EmployeeId DepartmentId PositionId StatusId
342 7 454 4
Employee Customers:
EmployeeId CustomerId
342 357
342 95
342 720
In this scenario, it would return Record 1 (because it matches the StatusId), Record 2 (because it matches both the DepartmentId and the PositionId), but it would not return Record 3 because it only matches the PositionId and not the StatusId, and it would return RecordId 4 because one of the Employee CustomerIds matches the CustomerId on Record 4.
I got part of this answer on another question enter link description here (please forgive me I am new and trying to figure out how to ask everything I need to know), but I can't figure out how to handle the multi-records.
I tried selecting the Employees customer Id's into a table variable and then attempted to use the Coalesce like this:
Declare #Customers table(CustomerId int)
INSERT INTO #Customers(CustomerId)
SELECT DISTINCT S.CustomerId
FROM employee_Customers
Select * from tbl
WHERE
COAlesce(StatusId,#StatusId)=#StatusId AND
COALESCE(DepartmentId,#DepartmentId)=#DepartmentId AND
Coalesce(PositionId,#PositionId)=#PositionId AND
Coalesce(EmployeeCompanyId,#EmployeeCompanyId) = #EmployeeCompanyId AND
COALESCE((Select CustomerId from tbl_Requirement_to_Customer),(Select CustomerId from #Customers)) = (Select CustomerId from #Customers)
But I receive the error "Subquery Returned more than 1 value".
I have a possible solution you can try. I don't think it will be plug-and-play but hopefully you can adapt it to your situation. I am using just the data as presented in your temp table, your employee record and your Employee-customers correlation.
The basic logic is to join your temp table to the employee(s) using or condition, but then to get a count of populated values, and compare this count to a count of the number of matching values, which must be at least the first count and greater than zero.
This returns your desired output:
select t.*
from Temp t
left join emp e on e.DepartmentId=t.DepartmentId or e.PositionId=t.PositionId or e.EmployeeId=t.EmployeeId or e.StatusId=t.StatusId
outer apply (
select case when exists (
select * from EmployeeCustomers ec join emp e on e.EmployeeId=ec.EmployeeId where ec.CustomerID=t.CustomerId
) then 1 else 0 end CustomerIdMatch
)c
outer apply (
values (
Iif(t.departmentId is null,0,1) +
Iif(t.PositionId is null,0,1) +
Iif(t.EmployeeId is null,0,1) +
Iif(t.StatusId is null,0,1) +
c.CustomerIdMatch
))x(Cnt)
outer apply (
values (
Iif(t.departmentId=e.DepartmentId,1,0) +
Iif(t.PositionId=e.PositionId,1,0) +
Iif(t.EmployeeId=e.EmployeeId,1,0) +
Iif(t.StatusId=e.StatusId,1,0) +
c.CustomerIdMatch
))y(Cnt2)
where cnt2>=cnt and cnt2>0
See working DB<>Fiddle
I have a table which is shown on below
id locale roaming rent
301 NULL 18.00 NULL
300 NULL NULL 5.00
299 11.00 NULL NULL
298 NULL NULL 4.00
297 NULL 20.00 NULL
296 NULL NULL 6.00
295 9.00 NULL NULL
294 NULL 20.00 NULL
293 10.00 NULL NULL
I want to get only one value in each column (without id column) in a row. How can I do it? BUT I want to do it only in one select query.
A possible solution:
SELECT (
SELECT TOP 1 [local]
FROM tbl
WHERE [local] IS NOT NULL
ORDER BY [id] DESC
) AS [local]
,(
SELECT TOP 1 [roaming]
FROM tbl
WHERE [roaming] IS NOT NULL
ORDER BY [id] DESC
) AS [roaming]
,(
SELECT TOP 1 [rent]
FROM tbl
WHERE [rent] IS NOT NULL
ORDER BY [id] DESC
) AS [rent]
I do it so
DECLARE #locale AS DECIMAL(11,2)
DECLARE #roaming AS DECIMAL(11,2)
DECLARE #rent AS DECIMAL(11,2)
SELECT #locale=COALESCE(locale, #locale), #roaming=COALESCE(roaming, #roaming), #rent=COALESCE(rent, #rent) FROM my_table
SELECT #locale as locale, #roaming as roaming, #rent as rent
I got a table containing 3 columns:
NameColumn CategoryColumn QuantityColumn
Name1 Category1 5
Name2 Category1 8
Name3 Category1 10
Name4 Category2 3
Name5 Category2 15
Name6 Category2 7
I need to write a query to convert the above data into the following result set:
NameColumn CategoryColumn QuantityColumn
Category1 NULL NULL
Name1 NULL 5
Name2 NULL 8
Name3 NULL 10
Category2 NULL NULL
Name4 NULL 3
Name5 NULL 15
Name6 NULL 7
Is there anyway to do this without using cursors?
Thanks.
SELECT NameColumn, CategoryColumn, QuantityColumn
FROM
(
SELECT CategoryColumn AS NameColumn, NULL AS CategoryColumn, NULL AS QuantityColumn,
CategoryColumn AS _cat, 1 AS _iscat
FROM myTable
GROUP BY CategoryColumn
UNION ALL
SELECT NameColumn, NULL AS CategoryColumn, QuantityColumn,
CategoryColumn AS _cat, 0 AS _iscat
FROM myTable
) x
ORDER BY _cat, _iscat DESC
SQL Fiddle example
You could, but unless you add metadata columns to the output, it will not be of much use.
select NameColumn, CategoryColumn, QuantityColumn, 0 IsCategory
from tbl
union all
select distinct CategoryColumn, CategoryColumn, null, 1
from tbl
order by CategoryColumn, IsCategory desc, NameColumn;
Sure, to get your EXACT output order, you can omit and NULLify certain columns, e.g.
select NameColumn, NULL as CategoryColumn, QuantityColumn
from (
select NameColumn, CategoryColumn, QuantityColumn, 0 IsCategory
from tbl
union all
select distinct CategoryColumn, CategoryColumn, null, 1
from tbl
) X
order by X.CategoryColumn, IsCategory desc, NameColumn;
But if you're intending to use the data outside of SQL Server, you'll want to keep the metadata to relate names and the categories to the original categories they came from. The IsCategory will also be useful to identify the headers that were picked out of the CategoryColumn.
I have 2 tables
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
NULL Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL William Stephen NULL 45 NULL NULL NULL HJD ABC
NULL Victor NULL Anthony NULL NULL NULL NULL NULL NULL
Table B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
I need to get the distinct records of (FirstName,MiddleName,LastName) of Table A and insert the same details along with the other fields matching with Table A into Table B.
My Table B has NameID as an identity coloum. So after inserting a unique record into Table B, I need to get that NameID and insert it back into Table A shown below :
TABLE A
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
3 William Stephen NULL 45 NULL NULL NULL HJD ABC
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL
TABLE B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC NULL NULL NULL
2 John NULL Keith NULL NULL 2344 NULL SQE FDG NULL NULL NULL
3 William Stephen NULL 45 NULL NULL NULL HJD ABC NULL NULL NULL
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL NULL NULL NULL
Can you please help me with this. Im not able to get this query right. Code in SQL Server 2008
Thanks in advance,
Sunitha
I think the easiest way to do this is with two queries. The first problem is handling duplicates in TableA. The following query selects an arbitrary row for each name combination:
insert into TableB()
select ()
from (select a.*,
row_number() over (partition by FirstName, MiddleName, LastName order by FirstName) as seqnum
from TableA a
) a
where seqnum = 1
Then, update the original table:
update TableA
set NameId = (select max(NameId) from TableB
where TableB.FirstName = TableA.FirstName and
TableB.MiddleName = TableA.MiddleName and
TableB.LastName = TableA.LastName
)
where NameId is null
If your fields contain NULL values (rather than blanks), you can use coalesce() for the join conditions:
update TableA
set NameId = (select max(NameId) from TableB
where coalesce(TableB.FirstName, '<null>') = coalesce(TableA.FirstName, '<null>') and
coalesce(TableB.MiddleName, '<null>') = coalesce(TableA.MiddleName, '<null>') and
coalesce(TableB.LastName , '<null>')= coalesce(TableA.LastName, '<null>')
)
where NameId is null
DECLARE #results TABLE
(
NameID INT,
FirstName VARCHAR(32), -- guessing on data types for these columns
MiddleName VARCHAR(32),
LastName VARCHAR(32)
);
;WITH x AS
(
SELECT FirstName, MiddleName, LastName,
rn = ROW_NUMBER() OVER (PARTITION BY FirstName, MiddleName, LastName
ORDER BY (SELECT NULL)
) --, ... other columns ...
FROM dbo.TableA
)
INSERT dbo.TableB
(
FirstName, MiddleName, LastName --, ... other columns ...
)
OUTPUT
inserted.NameID, inserted.FirstName,
inserted.MiddleName, inserted.LastName
INTO #results
SELECT FirstName, MiddleName, LastName --, ... other columns ...
FROM x WHERE rn = 1;
UPDATE a SET NameID = r.NameID
FROM dbo.TableA AS a
INNER JOIN #results AS r
ON COALESCE(a.FirstName,'') = COALESCE(r.FirstName,'')
AND COALESCE(a.MiddleName,'') = COALESCE(r.MiddleName,'')
AND COALESCE(a.LastName,'') = COALESCE(r.LastName,'');