SQL Server 2008R2 from Table to edges for Graph - sql-server

I have stumbled upon an interesting challenge. I have data in a SQL Server table with the following format/content.
Date | Name
---------+---------
1/1/2010 | John
1/1/2010 | Mark
1/1/2010 | Peter
1/1/2010 | Mia
2/4/2010 | John
2/4/2010 | Billy
I am trying to convert that table into a file containing edges of a graph.
I'll need the edges file to have to columns and all the combinations that the table shows.
John | Mark
John | Peter
John | Mia
Mark | Mia
Mark | Peter
Peter | Mia
John | Billy
I suspect part of this can be achieved with pivot/unpivot but don't know how to proceed with limiting the pivot to only two columns.
Also, I don't know how to make sure I get all the possible combinations of nodes, see that the first four 'nodes' need to become six 'edges.'

You could use ROW_NUMBER and "triangle join":
WITH cte AS
(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY date ORDER BY Name)
FROM tab
)
SELECT c.Name, c2.Name
FROM cte c
JOIN cte c2
ON c.Date = c2.Date
AND c.rn < c2.rn;
LiveDemo
Output:
╔═══════╦═══════╗
║ Name ║ Name ║
╠═══════╬═══════╣
║ John ║ Mark ║
║ John ║ Mia ║
║ John ║ Peter ║
║ Mark ║ Mia ║
║ Mark ║ Peter ║
║ Mia ║ Peter ║ -- ORDER BY based on `Name`
║ Billy ║ John ║ -- same here `B` before `J`
╚═══════╩═══════╝
Note:
To get stable sort you need to add column that will indicate order within group with the same date. I used Name but it swaps the names in last two rows.
Version with ID column:
CREATE TABLE tab(ID INT IDENTITY(1,1)
,Date DATE NOT NULL
,Name VARCHAR(6) NOT NULL);
INSERT INTO tab(Date,Name)
VALUES ('1/1/2010','John'), ('1/1/2010','Mark'), ('1/1/2010','Peter')
,('1/1/2010','Mia'), ('2/4/2010','John'),('2/4/2010','Billy');
WITH cte AS
(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY date ORDER BY ID)
FROM tab
)
SELECT c.Name, c2.Name
FROM cte c
JOIN cte c2
ON c.Date = c2.Date
AND c.rn < c2.rn;
LiveDemo 2
Output:
╔═══════╦═══════╗
║ Name ║ Name ║
╠═══════╬═══════╣
║ John ║ Mark ║
║ John ║ Peter ║
║ John ║ Mia ║
║ Mark ║ Peter ║
║ Mark ║ Mia ║
║ Peter ║ Mia ║
║ John ║ Billy ║
╚═══════╩═══════╝

Related

T-SQL: I'm trying to get the latest row of a column but also the sum of another column

I have the following table, it displays the SalesQty and the StockQty grouped by Article, Supplier, Branch and Month.
╔════════╦════════╦══════════╦═════════╦══════════╦══════════╗
║ Month ║ Branch ║ Supplier ║ Article ║ SalesQty ║ StockQty ║
╠════════╬════════╬══════════╬═════════╬══════════╬══════════╣
║ 201811 ║ 333 ║ 2 ║ 3122 ║ 4 ║ 11 ║
║ 201811 ║ 345 ║ 1 ║ 1234 ║ 2 ║ 10 ║
║ 201811 ║ 345 ║ 1 ║ 4321 ║ 3 ║ 11 ║
║ 201812 ║ 333 ║ 2 ║ 3122 ║ 2 ║ 4 ║
║ 201812 ║ 345 ║ 1 ║ 1234 ║ 3 ║ 12 ║
║ 201812 ║ 345 ║ 1 ║ 4321 ║ 4 ║ 5 ║
║ 201901 ║ 333 ║ 2 ║ 3122 ║ 1 ║ 8 ║
║ 201901 ║ 345 ║ 1 ║ 1234 ║ 6 ║ 9 ║
║ 201901 ║ 345 ║ 1 ║ 4321 ║ 2 ║ 8 ║
║ 201902 ║ 333 ║ 2 ║ 3122 ║ 7 ║ NULL ║
║ 201902 ║ 345 ║ 1 ║ 1234 ║ 4 ║ 13 ║
║ 201902 ║ 345 ║ 1 ║ 4321 ║ 1 ║ 10 ║
╚════════╩════════╩══════════╩═════════╩══════════╩══════════╝
Now I want to sum the SalesQty and get the latest StockQty and group them by Article, Supplier, Branch.
The final result should look like this:
╔════════╦══════════╦═════════╦═════════════╦════════════════╗
║ Branch ║ Supplier ║ Article ║ SumSalesQty ║ LatestStockQty ║
╠════════╬══════════╬═════════╬═════════════╬════════════════╣
║ 333 ║ 2 ║ 3122 ║ 14 ║ NULL ║
║ 345 ║ 1 ║ 1234 ║ 15 ║ 13 ║
║ 345 ║ 1 ║ 4321 ║ 10 ║ 10 ║
╚════════╩══════════╩═════════╩═════════════╩════════════════╝
I already tried this but it gives me an error, and i have no idea what i have to do in this case.
I've made this example so you can try it by yourself. db<>fiddle
SELECT
Branch,
Supplier,
Article,
SumSalesQty = SUM(SalesQty),
-- my attempt
LatestStockQty = (SELECT StockQty FROM TestTable i
WHERE MAX(Month) = Month
AND TT.Branch = i. Branch
AND TT.Supplier = i.Branch
AND TT.Article = i.Branch)
FROM
TestTable TT
GROUP BY
Branch, Supplier, Article
Thank you for your help!
We can try using ROW_NUMBER here, to isolate the latest record for each group:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Branch, Supplier, Article
ORDER BY Month DESC) rn,
SUM(SalesQty) OVER (PARTITION BY Branch, Supplier, Article) SumSalesQty
FROM TestTable t
)
SELECT
Month,
Branch,
Supplier,
Article,
SumSalesQty,
StockQty
FROM cte
WHERE rn = 1;
Inside the CTE we compute, for each Branch/Supplier/Article group a row number value, starting with 1 for the most recent month. We also compute the sum of the sales quantity over the same partition. Then, we only need to select all rows from that CTE where the row number is equal to 1.
Demo
A similar approach but without the CTE
SELECT top 1 with ties
Branch
, Supplier
, Article
, SUM(SalesQty) OVER (PARTITION BY Branch, Supplier, Article) SumSalesQty
, tt.StockQty as LatestStockQty
FROM TestTable TT
order by ROW_NUMBER() OVER (PARTITION BY Branch, Supplier, Article ORDER BY Month DESC)

Conditional ordering in SQL Server

Table T:
ID | Name | Days
ID is the PK.
I do want to select * from T, order by ID descending, but on TOP to be those entries which have the ID between 1000 and 1004
select *
from T
order by Id descending // something like a *case*?
1004 - 1st / 1003 2nd / 1002 3rd / 1001 4th / 1000 5nd ... and then 6th should be the max Id, and after this all descending excepting the Ids between 1000 - 1004 which we already displayed on the TOP.
I would like to know also the linq statement.
SELECT *
FROM (VALUES (998), (999), (1000), (1001), (1002), (1003), (1004)) AS T(ID)
ORDER BY CASE
WHEN T.ID BETWEEN 1000 AND 1004 THEN 1
ELSE 2
END
, ID DESC;
Took sample data from Sick's answer.
How does this work?
It checks whether your ID is matching your criteria and assign it a
value of 1
Everything else will have 2
It will sort by this value first
Your IDs with matching criteria will ALWAYS come first
Then we sort leftovers by ID in DESC order
Output:
╔══════╗
║ ID ║
╠══════╣
║ 1004 ║
║ 1003 ║
║ 1002 ║
║ 1001 ║
║ 1000 ║
║ 999 ║
║ 998 ║
╚══════╝
I've made additional example, which generates 676.800 rows in our DB:
;WITH TestTable (ID)
AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.objects
CROSS JOIN sys.schemas
)
SELECT *
FROM TestTable AS T
ORDER BY CASE
WHEN T.ID BETWEEN 1000 AND 1004 THEN 1
ELSE 2
END
, ID DESC;
That's its result:
╔════════╗
║ ID ║
╠════════╣
║ 1004 ║
║ 1003 ║
║ 1002 ║
║ 1001 ║
║ 1000 ║
║ 676800 ║
║ 676799 ║
║ 676798 ║
║ 676797 ║
║ 676796 ║
║ 676795 ║
║ ... ║
║ 1006 ║
║ 1005 ║
║ 999 ║
║ 998 ║
║ ... ║
║ 1 ║
╚════════╝
Try this
select *
from T
order by case when ID between 1000 and 1004 then -100000 else id end,ID desc
Here -100000 is s random low value
Example :
SELECT *
FROM (VALUES (998),
(999),
(1000),
(1001),
(1002),
(1003),
(1004) ) tc (id)
ORDER BY CASE
WHEN ID BETWEEN 1000 AND 1004 THEN -100000
ELSE id
END,
ID DESC
SELECT column_name, column_name
FROM table_name
ORDER BY column_name ASC|DESC, column_name ASC|DESC;

Alpha Numeric ID's Formatting

I am generating an alpha numeric ID using the first three characters of field LastName and then an incrementing 3 digit number. The query I am using in MSSQL 2012 is:
SELECT LastName, FirstName,
UPPER(LEFT(LastName, 3)) + LEFT('000', LEN(ROW_NUMBER()
OVER (PARTITION BY LEFT(LastName, 3)ORDER BY LEFT(LastName, 3)) )+1)
+ CAST(ROW_NUMBER() OVER (PARTITION BY
LEFT(LastName, 3)ORDER BY LEFT(LastName, 3)) AS NVARCHAR(1000)) AS ID
FROM #Table
The query works relatively well however once the numeric increments to two rather than it ending 010 it is 00010. The same would be true once the numeric portion increments to three digits. For example, I would like an output like follows:
╔══════════╦═══════════╦══════════╗
║ LastName ║ FirstName ║ ID ║
╠══════════╬═══════════╬══════════╣
║ Jones ║ David ║ JON001 ║
║ Jones ║ David ║ JON002 ║
║ Jones ║ David ║ JON003 ║
║ Jones ║ David ║ JON004 ║
║ Smith ║ John ║ SMI001 ║
║ Smith ║ John ║ SMI002 ║
║ Smith ║ Robert ║ SMI003 ║
║ Smith ║ John ║ SMI004 ║
║ Smith ║ John ║ SMI005 ║
║ Smith ║ Robert ║ SMI006 ║
║ Smith ║ John ║ SMI007 ║
║ Smith ║ John ║ SMI008 ║
║ Smith ║ Robert ║ SMI009 ║
║ Smith ║ John ║ SMI010 ║
║ Smith ║ John ║ SMI011 ║
║ Smith ║ Robert ║ SMI012 ║
║ .. ║ .. ║ .. ║
║ Smith ║ James ║ SMI100 ║
╚══════════╩═══════════╩══════════╝
I'm not sure how to address the incrementing numerical fields. Thanks for any help you can provide.
From the right instead of the left?
SELECT *, UPPER(LEFT(LastName, 3)) + RIGHT('000' + CAST(ID AS VARCHAR(3)), 3)
FROM (
SELECT
LastName,
FirstName,
ROW_NUMBER() OVER (PARTITION BY LEFT(LastName, 3) ORDER BY LastName) AS ID
FROM #table
) T

Generate Alphanumeric Customer ID

I'm working on migrating data out of a legacy system. To accommodate our new system proper customer ID's need to be generated. I'm looking for a way to generate unique alphanumeric ID's based in part on the customer's last name. The convention to be used is the first 3 letters of the customers last name followed by a 3 digit identifier. For example:
Smith, John = SMI001
Smith, Robert = SMI002
Jones, David = JON001
The new system will auto-generate new customers id's going forward, I just need a script to run once against the database I have no idea where to even begin. Thanks for any help you can provide!
Test Data
DECLARE #Table TABLE(LastName NVARCHAR(50), FirstName NVARCHAR(50), ID NVARCHAR(50))
INSERT INTO #Table(LastName, FirstName) VALUES
('Smith', 'John'),('Smith', 'Robert'),('Jones', 'David'),('Smith', 'John'),
('Smith', 'John'),('Smith', 'Robert'),('Jones', 'David'),('Smith', 'John'),
('Smith', 'John'),('Smith', 'Robert'),('Jones', 'David'),('Smith', 'John'),
('Smith', 'John'),('Smith', 'Robert'),('Jones', 'David'),('Smith', 'John')
Query
SELECT LastName, FirstName,
UPPER(LEFT(LastName, 3)) + LEFT('000', LEN(ROW_NUMBER()
OVER (PARTITION BY LEFT(LastName, 3)ORDER BY LEFT(LastName, 3)) )+1)
+ CAST(ROW_NUMBER() OVER (PARTITION BY
LEFT(LastName, 3)ORDER BY LEFT(LastName, 3)) AS NVARCHAR(1000)) AS ID
FROM #Table
Result Set
╔══════════╦═══════════╦══════════╗
║ LastName ║ FirstName ║ ID ║
╠══════════╬═══════════╬══════════╣
║ Jones ║ David ║ JON001 ║
║ Jones ║ David ║ JON002 ║
║ Jones ║ David ║ JON003 ║
║ Jones ║ David ║ JON004 ║
║ Smith ║ John ║ SMI001 ║
║ Smith ║ John ║ SMI002 ║
║ Smith ║ Robert ║ SMI003 ║
║ Smith ║ John ║ SMI004 ║
║ Smith ║ John ║ SMI005 ║
║ Smith ║ Robert ║ SMI006 ║
║ Smith ║ John ║ SMI007 ║
║ Smith ║ John ║ SMI008 ║
║ Smith ║ Robert ║ SMI009 ║
║ Smith ║ John ║ SMI00010 ║
║ Smith ║ John ║ SMI00011 ║
║ Smith ║ Robert ║ SMI00012 ║
╚══════════╩═══════════╩══════════╝
Here is a query demonstrating how to generate such IDs:
SELECT FirstName,
LastName,
Code = UPPER(LEFT(LastName, 3))
+ RIGHT('000'
+ CAST(
Row_Number() OVER ( PARTITION BY LastName
ORDER BY FirstName)
AS NVARCHAR(3))
, 3)
FROM [Table1]

SSRS bar chart order by value

I have the following data:
-----------------
Name|Value|Type
-----------------
A | 110 | Daily
-----------------
A | 770 | Weekly
-----------------
B | 150 | Daily
-----------------
B | 700 | Weekly
-----------------
C | 120 | Daily
-----------------
C | 840 | Weekly
In SSRS bar chart, the Name will be X axis, the Value will be Y axis, the Type will be series.
What I need is the bar chart will sort by the Weekly Value descending, so the expected order should be:
C Weekly
C Daily
A Weekly
A Daily
B Weekly
B Daily
How to do that? In query or in SSRS chart setting?
You can use ROW_NUMBER() to generate sequential number for each Name based on the Value for Weekly type and which will then be the basis on how the records will be sorted.
WITH records
AS
(
SELECT Name,
ROW_NUMBER() OVER (ORDER BY Value DESC) rn
FROM tableName
WHERE Type = 'Weekly'
)
SELECT a.*
FROM tableName a
INNER JOIN records b
ON a.Name = b.Name
ORDER BY b.rn, a.Type DESC
SQLFiddle Demo
OUTPUT
╔══════╦═══════╦════════╗
║ NAME ║ VALUE ║ TYPE ║
╠══════╬═══════╬════════╣
║ C ║ 840 ║ Weekly ║
║ C ║ 120 ║ Daily ║
║ A ║ 770 ║ Weekly ║
║ A ║ 110 ║ Daily ║
║ B ║ 700 ║ Weekly ║
║ B ║ 150 ║ Daily ║
╚══════╩═══════╩════════╝

Resources