SQL to see if cells in group match - sql-server

I have a table where one column has repeating values that I need to group. I then want to see if all of the cells within a different column, but within the same grouping match, or more specifically if they don't match.
This is what I have so far.
SELECT Dashboard_Widget_Id, Position ????? AS Is_Different
FROM toolbox.Dashboard_Widget_Sizes
GROUP BY Dashboard_Widget_Id

Never mind. I think I figured it out.
SELECT Dashboard_Widget_Id, CAST(SIGN(COUNT(t.Dashboard_Widget_Id) - 1) AS BIT) AS Is_Different
FROM (
SELECT Dashboard_Widget_Id
FROM toolbox.Dashboard_Widget_Sizes
GROUP BY Dashboard_Widget_Id, Position
) t
GROUP BY Dashboard_Widget_Id

If you wanted to keep the "detail view", here are two approaches using windowing functions:
DECLARE #t5 TABLE (A INT, B INT, C VARCHAR(100))
INSERT INTO #t5 (A,B,C)
SELECT 1,1,'1,2,3,4'
UNION ALL SELECT 1,1,'1,2,3,4'
UNION ALL SELECT 2,1,'1,2,3,4'--
UNION ALL SELECT 2,1,'1,2,3,4'---- Same
UNION ALL SELECT 2,1,'1,2,3,4'--
UNION ALL SELECT 3,1,'1,2,7,4'--
UNION ALL SELECT 3,1,'1,2,3,4'---- Different
UNION ALL SELECT 3,1,'1,2,3,4'--
UNION ALL SELECT 4,1,'1,2,3,4'
--"Alternate Partitions are not equal":
SELECT A,B,C,
CASE WHEN COUNT(*) OVER (PARTITION BY A) <> COUNT(*) OVER (PARTITION BY A,C) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END IsDifferent
FROM #t5 t
--"DENSE_RANK() OVER Partition > 1":
SELECT
A,B,C,CASE WHEN MAX(D) OVER (PARTITION BY A) > 1 THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END IsDifferent
FROM
(SELECT A,B,C,DENSE_RANK() OVER (PARTITION BY A ORDER BY C) D
FROM #t5 t) a

Related

SQL Recursive CTE - Recursive member of a common table expression 'CTE' has multiple recursive references

I need to be able to "look back" during the execution of a CTE. Using the sample data below I need an additional column returned which represents the balance as of the BalanceDate column. So rowid 6 would return 30 because that is the balance as of 3/6/2015. Rowid 9 would return 80 because that is the last record closest to 9/30/2015. When I try to use the CTE as a derived table I receive the error
Msg 253, Level 16, State 1, Procedure
Recursive member of a common table expression 'CTE' has multiple recursive references.
--Sample data
CREATE TABLE #TEMP
(RowID int null,
TranDate date null,
Amount int null,
BalanceDate date null);
INSERT INTO #TEMP (RowID,TranDate, Amount, BalanceDate)
SELECT 1,'1/15/2015',10,null
UNION ALL
SELECT 2,'2/18/2015',10,null
UNION ALL
SELECT 3,'3/6/2015',10,null
UNION ALL
SELECT 4,'6/1/2015',10,null
UNION ALL
SELECT 5,'6/18/2015',10,null
UNION ALL
SELECT 6,'7/31/2015',10,'3/6/2015'
UNION ALL
SELECT 7,'8/2/2015',10,null
UNION ALL
SELECT 8,'9/13/2015',10,null
UNION ALL
SELECT 9,'11/15/2015',10,'9/30/2015';
with CTE
as
( SELECT RowID, TranDate, Amount, Balance=Amount, BalanceDate FROM #TEMP WHERE RowID = 1
UNION ALL
SELECT #TEMP.RowID,#TEMP.TranDate, #TEMP.Amount, Balance = #TEMP.Amount + CTE.Balance, #TEMP.BalanceDate
FROM #TEMP
INNER JOIN CTE on #Temp.RowID = CTE.RowID + 1
)
SELECT * FROM CTE;
I generally avoid recursive CTEs unless there's a reason you need to use them (for example, to find parents and children). Rather than using a recursive CTE, you can do something like this to get what you want:
EDIT:
If there are ever negative amounts, this would produce the correct result.
SELECT RowID, T1.TranDate, Amount
, (SELECT SUM(Amount) FROM #TEMP WHERE TranDate <= T1.TranDate) Balance
, BalanceDate
, (SELECT SUM(Amount) FROM #TEMP WHERE TranDate <= T1.BalanceDate) BalanceToDate
FROM #TEMP T1
Edited out other queries that were here before to make this less messy.
Using original recursive CTE:
; WITH CTE AS
(
SELECT RowID, TranDate, Amount, Balance=Amount, BalanceDate
FROM TEMP
WHERE RowID = 1
UNION ALL
SELECT TEMP.RowID,TEMP.TranDate, TEMP.Amount, Balance = TEMP.Amount + CTE.Balance, TEMP.BalanceDate
FROM TEMP
JOIN CTE on Temp.RowID = CTE.RowID + 1
)
SELECT C.RowID, C.TranDate, C.Amount, C.Balance, C.BalanceDate
, (SELECT SUM(Amount) FROM CTE WHERE TranDate <= C.BalanceDate) BalanceToDate
FROM CTE C

How to change duplicate values to unique values in a column using by stored procedure in SQL Server

I have to change RowSpan value if OccupationalInjuryId's duplicate value exists. Here is the screenshot:
Should I use T-SQL or #temporary table structure. But I couldn't figure out how to fix. For example there are two values with OccupationalId = 1100 and both rowspan's are 2. It should be 1 for second value's RowSpan no.
My SQL query:
select *
from
(select
Row_Number() over (order by oi.Id) as RowNo,
oid.OccupationalInjuryId
from
OTH_OccupationalInjury oi
left join
OTH_OccupationalInjuryDetail oid on oi.Id = oid.OccupationalInjuryId
where
1 = 1) t1
left join
(select
count(OccupationalInjuryId) end as RowSpan,
oid.OccupationalInjuryId
from
OTH_OccupationalInjury oi
left join
OTH_OccupationalInjuryDetail oid on oi.Id = oid.OccupationalInjuryId
where
1=1
group by
OccupationalInjuryId
having
(Count(OccupationalInjuryId) > 1) ) t2 on t1.OccupationalInjuryId = t2.OccupationalInjuryId
END
If I got you correctly, you only care about first RowSpan value for each OccupationalInjuryId and for others you want 1. If I am wrong, correct me.
So, Can you try something like :
DECLARE #BaseTable TABLE(ID INT, OccupationalInjuryId INT, RowSpan INT)
INSERT INTO #BaseTable (ID, OccupationalInjuryId, RowSpan)
SELECT 1, 1100, 2 UNION ALL
SELECT 2, 1100, 2 UNION ALL
SELECT 3, 1099, 2 UNION ALL
SELECT 4, 1099, 3 UNION ALL
SELECT 5, 1106, NULL
SELECT
ID,
OccupationalInjuryId,
CASE WHEN rn = 1 THEN RowSpan ELSE 1 END AS RowSpan
FROM
(
SELECT ID, OccupationalInjuryId, RowSpan, ROW_NUMBER() OVER (PARTITION BY OccupationalInjuryId, RowSpan ORDER BY ID) AS rn FROM #BaseTable
) tmp

How to find the maximum value in join without using if in sql stored procedure

I have a two tables like below
A
Id Name
1 a
2 b
B
Id Name
1 t
6 s
My requirement is to find the maximum id from table and display the name and id for that maximum without using case and if.
i findout the maximum by using below query
SELECT MAX(id)
FROM (SELECT id,name FROM A
UNION
SELECT id,name FROM B) as c
I findout the maximum 6 using the above query.but i can't able to find the name.I tried the below query but it's not working
SELECT MAX(id)
FROM (SELECT id,name FROM A
UNION
SELECT id,name FROM B) as c
How to find the name?
Any help will be greatly appreciated!!!
First combine the tables, since you need to search both. Next, determine the id you need. JOIN the id back with the temporarily created table to retreive the name that belongs to that id:
WITH tmpTable AS (
SELECT id,name FROM A
UNION
SELECT id,name FROM B
)
, max AS (
SELECT MAX(id) id
FROM tmpTable
)
SELECT t.id, t.name
FROM max m
JOIN tmpTable t ON m.id = t.id
You could use ROW_NUMBER(). You have to UNION ALL TableA and TableB first.
WITH TableA(Id, Name) AS(
SELECT 1, 'a' UNION ALL
SELECT 2, 'b'
)
,TableB(Id, Name) AS(
SELECT 1, 't' UNION ALL
SELECT 6, 's'
)
,Combined(Id, Name) AS(
SELECT * FROM TableA UNION ALL
SELECT * FROM TableB
)
,CTE AS(
SELECT *, RN = ROW_NUMBER() OVER(ORDER BY ID DESC) FROM Combined
)
SELECT Id, Name
FROM CTE
WHERE RN = 1
Just order by over the union and take first row:
SELECT TOP 1 * FROM (SELECT * FROM A UNION SELECT * FROM B) x
ORDER BY ID DESC
This won't show ties though.
For you stated that you used SQL Server 2008. Therefore,I used FULL JOIN and NESTED SELECT to get what your looking for. See below:
SELECT
(SELECT
1,
ISNULL(A.Id,B.Id)Id
FROM A FULL JOIN B ON A.Id=B.Id) AS Id,
(SELECT
1,
ISNULL(A.Name,B.Name)Name
FROM A FULL JOIN B ON A.Id=B.Id) AS Name
It's possible to use ROW_NUMBER() or DENSE_RANK() functions to get new numiration by Id, and then select value with newly created orderId equal to 1
Use:
ROW_NUMBER() to get only one value (even if there are some repetitions of max id)
DENSE_RANK() to get all equal max id values
Here is an example:
DECLARE #tb1 AS TABLE
(
Id INT
,[Name] NVARCHAR(255)
)
DECLARE #tb2 AS TABLE
(
Id INT
,[Name] NVARCHAR(255)
)
INSERT INTO #tb1 VALUES (1, 'A');
INSERT INTO #tb1 VALUES (7, 'B');
INSERT INTO #tb2 VALUES (4, 'C');
INSERT INTO #tb1 VALUES (7, 'D');
SELECT * FROM
(SELECT Id, Name, ROW_NUMBER() OVER (ORDER BY Id DESC) AS [orderId]
FROM
(SELECT Id, Name FROM #tb1
UNION
SELECT Id, Name FROM #tb2) as tb3) AS TB
WHERE [orderId] = 1
SELECT * FROM
(SELECT Id, Name, DENSE_RANK() OVER (ORDER BY Id DESC) AS [orderId]
FROM
(SELECT Id, Name FROM #tb1
UNION
SELECT Id, Name FROM #tb2) as tb3) AS TB
WHERE [orderId] = 1
Results are:

SQL Server: Replace characters different than a char in a string using no temporary table

I have a NVARCHAR(10) column in a table. It can store any type of UNICODE strings.
I want to replace every char which is different than '1' with '0'.
Let's say I have the string '012345C18*'. I should get '0100000100'.
I managed to do it using a helper table which contains indexes from 1 to the size of my column (10),
like this:
CREATE TABLE HELP(Idx INT)
INSERT INTO HELP
SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
DECLARE #myStr VARCHAR(10)
SET #myStr = '012345C18*'
SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
SELECT SUBSTRING(A.Val,H.Idx,1) AS Ch
FROM
(SELECT #myStr AS Val) A
CROSS JOIN HELP H
)B FOR XML PATH('')),1,0,'')
It works, but can it be done in a nicer way? This seems ugly for a simple update, ignoring the fact that the size of the column can change over time.
It also has to run on SQL >=2005.
SQL Fiddle here
Thanks!
A slightly different approach, using a recursive query:
WITH cte AS
( SELECT v, i = 0,
nv = CAST('' AS NVARCHAR(10))
FROM t
UNION ALL
SELECT v, i+1,
CAST(nv + CASE WHEN SUBSTRING(v, i+1, 1) = '1' THEN '1' ELSE '0' END
AS NVARCHAR(10))
FROM cte
WHERE i+1 <= LEN(v)
)
SELECT v, nv
FROM cte
WHERE i = LEN(v) ;
Tested in SQLFiddle
Here is a way to do this with a cte. In my system I actually have the ctes as a view name cteTally. This technique generates a 10,000 row view with zero reads. ;) Your code as posted works quite well. For this example I moved the string into a table since that is what you are working with in the real system.
declare #myStrings table(MyVal varchar(10));
insert #myStrings
select '012345C18*';
WITH
E1(N) AS (select 1 from
(
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1) union all
select (1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
SELECT STUFF((SELECT '' + CASE(B.Ch) WHEN '1' THEN '1' ELSE '0' END FROM (
SELECT SUBSTRING(A.MyVal, t.N, 1) AS Ch
FROM
#myStrings A
CROSS JOIN cteTally t
where t.N < LEN(a.MyVal)
)B FOR XML PATH('')),1,0,'')
If you want to update a whole table a UDF might be useful.
Create FUNCTION dbo.F_MakeBinary(#Param NVarchar(max))
RETURNS NVarchar (max)
AS
BEGIN
DECLARE #a NVarchar(max)
Set #a=#Param
While PATINDEX(N'%[^0-1]%', #a) > 0
begin
select #a=STUFF(#a, PATINDEX(N'%[^0-1]%', #a),1,'0')
end
Return #a
END
Usage:
Update aTable Set aField = dbo.F_MakeBinary(aField)

SQL Server 2008 : select members group by with conditions

I have a table
CREATE TABLE #tblA (mem_id int, type varchar(20), address1 varchar(20),group_id int)
insert into #tblA (mem_id, type, address1,group_id)
values (1,'self','abc St',1),
(2,'Child','abc St',1),
(3,'Child','xyz st',1),
(4,'spouse','pqr st',1),
(5,'Child','abc St',1),
(6,'Child','xyz st',1),
(7,'self','mno st',2),
(8,'Child','def St',2),
(9,'Child','def st',2),
(10,'self','loi st',3),
(11,'Child','loi St',3),
(12,'Child','ewr st',3)
(13,'self','ooo st',NULL),
So, I want to select:
For every group, select "self".
Select self if it has no group.
If other people live at different address than self, select those
members.
if multiple members live at different address, pick 1 member living
at a different address
So expected result:
(1,'self','abc St',1),
(3,'Child','xyz st',1),
(4,'spouse','pqr st',1),
(7,'self','mno st',2),
(8,'Child','def St',2),
(10,'self','loi st',3),
(12,'Child','ewr st',3)
(13,'self','ooo st',NULL),
Thanks
My code: not working:
select
mem_id
from
(select
a.*,
RANK() over (partition by group_id order by AddressCnt DESC) as AddressRank
from
(select
a.*,
(case when max(address1) over (partition by group_id) = min(address1) over (partition by group_id)
then 1 else 0
end) as AddressSame,
count(*) over (partition by group_id, address1) as AddressCnt
from #tblA a) a
) a
where
(AddressSame = 1 or type in ('Self') or
(AddressRank > 1 OR type in ('Self') or group_id is null
Here's a solution.
Create a CTE identifying the "self" records.
Create another cte identifying the "others" with a join from self to tableA
Use row_number to find just one record for the other
union the two cte's
WITH self
AS (SELECT *
FROM tbla
WHERE type = 'self'),
others
AS (SELECT a.mem_id,
a.type,
a.address1,
a.group_id,
Row_number()
OVER (
partition BY a.group_id, a.address1
ORDER BY a.mem_id) rn
FROM self s
INNER JOIN tbla a
ON s.group_id = a.group_id
AND s.mem_id <> a.mem_id --Exclude self records
AND s.address1 <> a.address1 --Exclude the same address as self)
SELECT mem_id,
type,
address1,
group_id
FROM self
UNION
SELECT mem_id,
type,
address1,
group_id
FROM others
WHERE rn = 1
ORDER BY mem_id
DEMO
This MySQL query gives the desired output:
select * from tblA having type='self' UNION select * from tblA group by address1 having address1 not in (select address1 from tblA where type='self') ORDER by mem_id;

Resources