Compare two number SQL - sql

In SQL,I am trying to compare two numbers in the same field. Both numbers contain different information, but for some technical reason they are same. The problem is when exist one sub-string of length 5 and another of length 4 and the last 4 digits of both are same.I want to get the first one with length 5.
Example:
--------------------------------
|ID | Number| Description |
---------------------------------
| 1 | 12345 | Project X,Ready |
---------------------------------
| 2 | 2345 | Project X,onDesign |
---------------------------------
I should always get 12345(or biggest one) if exist numbers with last 4 digits same. Is there any CASE or CTE statement which can give me an easy resolution for this issue?

Try this:
SELECT Id
,Number
,Description
FROM (
SELECT Id
,Number
,Description
,rank() OVER (PARTITION BY right(cast([Number] AS VARCHAR(20)), 4) ORDER BY Number DESC) AS Ranking
FROM YourTable
) InnerTable
WHERE ranking = 1

Here is an example with not exists:
DECLARE #t TABLE
(
ID INT ,
Number INT ,
Description VARCHAR(100)
)
INSERT INTO #t
VALUES ( 1, 12345, 'Project 1' ),
( 2, 2345, 'Project 2' ),
( 3, 77777, 'Project 3' ),
( 4, 7777, 'Project 4' ),
( 5, 88888, 'Project 5' ),
( 6, 9999, 'Project 6' )
SELECT * FROM #t t1
WHERE NOT EXISTS(SELECT * FROM #t t2
WHERE t2.ID <> t1.ID AND
CAST(t2.Number AS VARCHAR(10)) LIKE '%' + CAST(t1.Number AS VARCHAR(10)))
Output:
ID Number Description
1 12345 Project 1
3 77777 Project 3
5 88888 Project 5
6 9999 Project 6

So you need to join using last 4 digits. You could do this by using simple MOD operator. It's used as a percentage sign in SQL Server.
SELECT 12345 % 10000;
This outputs 2345. Exactly what we are looking for.
So we could build the following query to use that calculation:
DECLARE #Test TABLE
(
ID INT
, Number INT
, Description VARCHAR(500)
);
INSERT INTO #Test(ID, Number, Description)
VALUES (1, 12345, 'Project X,Ready')
, (2, 2345, 'Project X,onDesign');
SELECT T1.*
FROM #Test AS T1
INNER JOIN #Test AS T2
ON T2.Number = T1.Number % 10000
WHERE T2.Number <> T1.Number;
Output:
╔════╦════════╦═════════════════╗
║ ID ║ Number ║ Description ║
╠════╬════════╬═════════════════╣
║ 1 ║ 12345 ║ Project X,Ready ║
╚════╩════════╩═════════════════╝
Note that I've added WHERE T2.Number <> T1.Number. It eliminates equal numbers, because SELECT 2345 % 10000 is 2345 as well.
Update
This could be done using ROW_NUMBER()
;WITH Data (ID, Number, Description, RN)
AS (
SELECT ID
, Number
, Description
, ROW_NUMBER() OVER (PARTITION BY Number % 10000 ORDER BY Number DESC)
FROM #Test
)
SELECT *
FROM Data
WHERE RN = 1;
This will do the classic row_number stuff. It will partition windows by Number % 10000, which means that 12345 and 2345 will fall under same window and the highest number will always come first.

Try this:
SELECT DISTINCT A.*
FROM [Tablename] AS A
INNER JOIN [Tablename] AS B
ON B.Number =RIGHT(A.Number,4)
WHERE B.Number <> A.Number;
RIGHT(A.Number,4) will compare the last 4 digits and will give the output

The query might be RDBMS spesific. For example with MSSQL you can do like this:
SELECT *
FROM myTable AS d1
WHERE NOT EXISTS ( SELECT *
FROM myTable AS d2
WHERE SUBSTRING(d2.number, 2, 4) = d1.number );
EDIT: Ah, you edited and it is an INT! Then you can use the % operator instead of substring.
Sample with CTE:
DECLARE #dummy TABLE
(
id INT IDENTITY
PRIMARY KEY ,
number INT ,
[description] VARCHAR(20)
);
INSERT #dummy ( [number], [description] )
VALUES ( 12345, 'P' ),
( 22345, 'P' ),
( 2345, 'P' ),
( 3456, 'P' ),
( 13456, 'P' ),
( 4567, 'P' );
WITH d AS (
SELECT MAX(number) AS maxNum
FROM #dummy AS [d]
GROUP BY [d].[number] % 10000
)
SELECT d1.*
FROM #dummy AS [d1]
INNER JOIN d ON d.[maxNum] = d1.[number];

Related

Get Records depend on their sum value

I have a SQL Server table which have records like this
ID | Value
1 | 100
2 | 150
3 | 250
4 | 600
5 | 1550
6 | 50
7 | 300
I need to select random records, but the only condition is that the total sum of this records value achieve a specific number or percentage i define.
let's say i need a total value of 300 or 10%, so here are the chances
1 | 100
2 | 150
6 | 50
or
3 | 250
6 | 50
or
7 | 300
can any one help me to do this.
Think this recursive CTE works, no idea what the performance will be like though once you get past a trivial amount of rows:
DECLARE #Test TABLE
(
ID INT NOT NULL,
VAL INT NOT NULL
);
INSERT INTO #Test
VALUES (1,100),
(2,150),
(3,250),
(4,600),
(5,1550),
(6,50),
(7,300);
DECLARE #SumValue INT = 300,
#Percentage INT = 10;
WITH GetSums
AS
(
SELECT T.ID,
T.Val,
CAST(T.ID AS VARCHAR(MAX)) AS IDs
FROM #Test AS T
UNION ALL
SELECT T1.ID,
T1.Val + GS.Val AS Val,
CAST(T1.ID AS VARCHAR(MAX)) + ',' + GS.IDs AS IDs
FROM #Test AS T1
INNER
JOIN GetSums AS GS
ON T1.ID > GS.ID
)
SELECT GS.IDs,
GS.Val
FROM GetSums AS GS
WHERE (GS.Val = #SumValue OR GS.VAL = (SELECT SUM(Val) FROM #Test AS T) / #Percentage)
OPTION (MAXRECURSION 50);
Similar found here:
find all combination where Total sum is around a number
Try this...we will get the correct answer if the 6th value is 250...
SELECT 1 ID, 100 Value
INTO #Temp_1
UNION ALL SELECT 2 , 150
UNION ALL SELECT 2 , 150
UNION ALL SELECT 3 , 250
UNION ALL SELECT 4 , 600
UNION ALL SELECT 5 , 1550
UNION ALL SELECT 6 , 250
UNION ALL SELECT 7 , 300
CREATE TABLE #Temp_IDs
(
ID Int,
Value Numeric(18,2)
)
DELETE
FROM #Temp_IDs
DECLARE #ID Int,
#Vale Numeric(18,2),
#ContinueYN Char(1)
SET #ContinueYN = 'Y'
IF EXISTS (SELECT TOP 1 1 FROM #Temp_1
WHERE Value <= 300
AND ID NOT IN (SELECT ID FROM #Temp_IDs )
AND Value <= (SELECT 300 - ISNULL( SUM(Value),0) FROM #Temp_IDs)
ORDER BY NEWID())
BEGIN
WHILE (#ContinueYN = 'Y')
BEGIN
SELECT #ID = ID,
#Vale = Value
FROM #Temp_1
WHERE Value <= 300
AND ID NOT IN (SELECT ID FROM #Temp_IDs )
AND Value <= (SELECT 300 - ISNULL( SUM(Value),0) FROM #Temp_IDs)
ORDER BY NEWID()
INSERT INTO #Temp_IDs
SELECT #ID,#Vale
IF (SELECT SUM(Value) FROM #Temp_IDs) = 300
BREAK
ELSE IF #ID IS NULL
BEGIN
DELETE FROM #Temp_IDs
END
SET #ID = NULL
SET #Vale = NULL
END
END
SELECT *
FROM #Temp_IDs
DROP TABLE #Temp_IDs
DROP TABLE #Temp_1

How can I select distinct by one column?

I have a table with the columns below, and I need to get the values if COD is duplicated, get the non NULL on VALUE column. If is not duplicated, it can get a NULL VALUE. Like the example:
I'm using SQL SERVER.
This is what I get:
COD ID VALUE
28 1 NULL
28 2 Supermarket
29 1 NULL
29 2 School
29 3 NULL
30 1 NULL
This is what I want:
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
What I'm tryin' to do:
;with A as (
(select DISTINCT COD,ID,VALUE from CodId where ID = 2)
UNION
(select DISTINCT COD,ID,NULL from CodId where ID != 2)
)select * from A order by COD
You can try this.
DECLARE #T TABLE (COD INT, ID INT, VALUE VARCHAR(20))
INSERT INTO #T
VALUES(28, 1, NULL),
(28, 2 ,'Supermarket'),
(29, 1 ,NULL),
(29, 2 ,'School'),
(29, 3 ,NULL),
(30, 1 ,NULL)
;WITH CTE AS (
SELECT *, RN= ROW_NUMBER() OVER (PARTITION BY COD ORDER BY VALUE DESC) FROM #T
)
SELECT COD, ID ,VALUE FROM CTE
WHERE RN = 1
Result:
COD ID VALUE
----------- ----------- --------------------
28 2 Supermarket
29 2 School
30 1 NULL
Another option is to use the WITH TIES clause in concert with Row_Number()
Example
Select top 1 with ties *
from YourTable
Order By Row_Number() over (Partition By [COD] order by Value Desc)
Returns
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
I would use GROUP BY and JOIN. If there is no NOT NULL value for a COD than it should be resolved using the OR in JOIN clause.
SELECT your_table.*
FROM your_table
JOIN (
SELECT COD, MAX(value) value
FROM your_table
GROUP BY COD
) gt ON your_table.COD = gt.COD and (your_table.value = gt.value OR gt.value IS NULL)
If you may have more than one non null value for a COD this will work
drop table MyTable
CREATE TABLE MyTable
(
COD INT,
ID INT,
VALUE VARCHAR(20)
)
INSERT INTO MyTable
VALUES (28,1, NULL),
(28,2,'Supermarket'),
(28,3,'School'),
(29,1,NULL),
(29,2,'School'),
(29,3,NULL),
(30,1,NULL);
WITH Dups AS
(SELECT COD FROM MyTable GROUP BY COD HAVING count (*) > 1 )
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
INNER JOIN dups ON MyTable.COD = Dups.COD
WHERE value IS NOT NULL
UNION
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
LEFT JOIN dups ON MyTable.COD = Dups.COD
WHERE dups.cod IS NULL

How to get comma delimited CategoryIds recursively?

I have a table like this:
Name CategoryId ParentCategoryId
Footwear 93 0
Men Shoes 6 93
Female Shoes 7 93
Mobile 2 0
Smartphone 4 2
I need output like:
Name Categories
Footwear 93,0
Men Shoes 6,93,0
Female Shoes 7,93,0
Mobile 2,0
Smartphone 4,2,0
Basically, I need to recursively get the category ids and make them into a comma delimited string. I am getting into SQL after 3 years now and I have no idea how to get this result. I have tried solutions from other SO questions but still no luck.
You do this with recursive cte:
DECLARE #t TABLE
(
Name VARCHAR(100) ,
CategoryId INT ,
ParentCategoryId INT
)
INSERT INTO #t
VALUES ( 'Footwear', 93, 0 ),
( 'Men Shoes', 6, 93 ),
( 'Female Shoes', 7, 93 ),
( 'Mobile', 2, 0 ),
( 'Smartphone', 4, 2 );
WITH cte
AS ( SELECT * ,
CAST(CategoryId AS VARCHAR(100)) AS Categories
FROM #t
WHERE ParentCategoryId = 0
UNION ALL
SELECT t.* ,
CAST(CAST(t.CategoryId AS VARCHAR(100)) + ','
+ c.Categories AS VARCHAR(100))
FROM #t t
JOIN cte c ON c.CategoryId = t.ParentCategoryId
)
SELECT *
FROM cte
Try it with a recursive CTE:
DECLARE #tbl TABLE(Name VARCHAR(100),CategoryId INT,ParentCategoryId INT);
INSERT INTO #tbl VALUES
('Footwear',93,0)
,('Men Shoes',6,93)
,('Female Shoes',7,93)
,('Mobile',2,0)
,('Smartphone',4,2);
--based on this: http://stackoverflow.com/a/5522641/5089204
WITH tree (CategoryId, ParentCategoryId, level, Name, rn, IdList) as
(
SELECT CategoryId, ParentCategoryId, 0 as level, Name,
convert(varchar(max),right(row_number() over (order by CategoryId),10)) AS rn,
convert(varchar(max),ISNULL(CategoryId,0)) AS IdList
FROM #tbl
WHERE ParentCategoryId = 0
UNION ALL
SELECT c2.CategoryId, c2.ParentCategoryId, tree.level + 1, c2.Name,
rn + '/' + convert(varchar(max),right(row_number() over (order by tree.CategoryId),10)),
convert(varchar(max),c2.CategoryId) + ',' + IdList
FROM #tbl c2
INNER JOIN tree ON tree.CategoryId = c2.ParentCategoryId
)
SELECT *
FROM tree
order by RN
Part of the result:
1 Mobile 2
1/1 Smartphone 4,2
2 Footwear 93
2/1 Men Shoes 6,93
2/2 Female Shoes 7,93

Merging data in a single SQL table without a Cursor

I have a table with an ID column and another column with a number. One ID can have multiple numbers. For example
ID | Number
1 | 25
1 | 26
1 | 30
1 | 24
2 | 4
2 | 8
2 | 5
Now based of this data, in a new table, I want to have this
ID | Low | High
1 | 24 | 26
1 | 30 | 30
2 | 4 | 5
2 | 8 | 8
As you can see, I want to merge any data where the numbers are consecutive, like 24, 25, 26. So now the low was 24, the high was 26, and then 30 is still a separate range. I am dealing with large amounts of data, so I would prefer to not use a cursor for performance sake (which is what I was previously doing, and was slowing things down quite a bit)...What is the best way to achieve this? I'm no SQL pro, so I'm not sure if there is a function available that could make this easier, or what the fastest way to accomplish this would be.
Thanks for the help.
The key observation is that a sequence of numbers minus another sequence is a constant. We can generate another sequence using row_number. This identifies all the groups:
select id, MIN(number) as low, MAX(number) as high
from (select t.*,
(number - ROW_NUMBER() over (partition by id order by number) ) as groupnum
from t
) t
group by id, groupnum
The rest is just aggregation.
Solution with CTE and recursion:
WITH CTE AS (
SELECT T.ID, T.NUMBER, T.NUMBER AS GRP
FROM T
LEFT OUTER JOIN T T2 ON T.ID = T2.ID AND T.NUMBER -1 = T2.NUMBER
WHERE T2.ID IS NULL
UNION ALL
SELECT T.ID, T.NUMBER, GRP
FROM CTE
INNER JOIN T
ON T.ID = CTE.ID AND T.NUMBER = CTE.NUMBER + 1
)
SELECT ID, MAX( NUMBER ), MIN(NUMBER)
FROM CTE
GROUP BY ID, GRP
Results at fiddlesql
I'd suggest using a WHILE loop structure with a table variable instead of the cursor.
For example,
DECLARE #TableVariable TABLE
(
MyID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
[ID] int,
[Number] int
)
DECLARE #Count int, #Max int
INSERT INTO #TableVariable (ID, Number)
SELECT ID, Number
FROM YourSourceTable
SELECT #Count = 1, #Max = MAX(MyID)
FROM #TableVariable
WHILE #Count <= #Max
BEGIN
...do your processing here...
SET #Count = #Count + 1
END
CREATE TABLE Table1
([ID] int, [Number] int)
;
INSERT INTO Table1
([ID], [Number])
VALUES
(1, 25),
(1, 26),
(1, 30),
(1, 24),
(2, 4),
(2, 8),
(2, 5)
;
select ID,
MIN(Number)
,(SELECT MIN(Number)
FROM (SELECT TOP 2 Number from Table1 WHERE ID =
T1.Id ORDER BY Number DESC) as DT)
from Table1 as T1
GROUP BY ID
UNION
SELECT ID, MAX(Number), MAX(Number)
FROM Table1 as T1
GROUP BY ID;
Live Example

Displaying occurrences of NULL values and overall duplicates with SQL

With data such as the below, I need to generate a report that reports back the number of records with NULL and the number of duplicates, all with one SQL query if possible.
DES | VAL
--------------
Tango | 32
Zulu | [null]
Golf | 12
Golf | 12
Bravo | [null]
The report would look like:
NULLS | DUPLICATES
---------------------
2 | 1
I can get the nulls with something like SUM(CASE VAL WHEN NULL THEN 1 ELSE 0 END) AS NULLS, and duplicates separately, but not as one query so I don't even know if it's possible.
SELECT
(SELECT COUNT(*) FROM table_name WHERE val IS NULL)
AS NULLS,
(SELECT ( COUNT(val) - COUNT(DISTINCT(val)) ) FROM table_name)
AS DUPLICATES
Not sure how you want to count your duplicates so I included two versions.
declare #T table
(
DES varchar(10),
VAL int
)
insert into #T values
('Tango', 32),
('Zulu', null),
('Zulu', null),
('Zulu', null),
('Golf', 12),
('Golf', 12),
('Bravo', null)
select sum(case when T.VAL is null then C end) as NULLS,
sum(case when T.C > 1 then C-1 end) as DUPLICATES1,
sum(case when T.C > 1 then 1 end) as DUPLICATES2
from (
select VAL, count(*) as C
from #T
group by DES, VAL
) T
Result:
NULLS DUPLICATES1 DUPLICATES2
----------- ----------- -----------
4 3 2
Well if you have 2 selects returning scalar values that you want to combine into a simple report like that, you could do:
SELECT
2 AS NULLS,
DUPS
FROM (SELECT 1 AS DUPS) D
Results:
NULLS DUPS
----------- -----------
2 1
Replacing the two selects as needed.
Assuming (?!) that you want to count duplicate rows, this may come close to what you want:
declare #Foo as Table ( DES VarChar(10), VAL Int Null )
insert into #Foo ( DES, VAL ) values
( 'Tango', 32 ),
( 'Zulu', NULL ),
( 'Golf', 12 ), ( 'Golf', 12 ), ( 'Golf', 13 ),
( 'Bravo', NULL ),
( 'Whiskey', 8388 ), ( 'Whiskey', 8388 ), ( 'Whiskey', 8388 ), ( 'Whiskey', 8388 )
select * from #Foo
select distinct DES, VAL from #Foo
select ( select Count( 42 ) from #Foo where VAL is NULL ) as [NULLS],
( select Count( 42 ) from #Foo ) - Count( 42 ) as [DUPLICATES] from ( select distinct DES, VAL from #Foo ) as Elmer