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
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
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
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
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
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