I m trying to select records and ordering it using a specific column's value.
Users are assigned to a specific district or area (are associated with districtid and areaid). A user with AreaID of 5 and DistirctID of 0 has edit access to all data within his/her Area which spans multiple districts. A user with AreaID of 5 and DistirctID of 8 has edit access to all data within this district only.
To prevent users from paging through data to find their data, I want to show theirs on top of the table. Don't know how to set the "order by" (order by districtid, but want districtid 8 to be on top). Is it possible?
This is used within an ASP.NET (C#) application.
Assuming I am trying to display data for a user assigned to AreaID 5 and DistrictID 8:
select col1, col2, ..., colN from SomeTable order by DistrictID ... (?)
Database is SQL Server 2016.
Update
this is what i tried:
select distinct ml.MPOOID, ml.MPOOName, ml.AreaID, ml.DistrictID,
zc.AreaName, zc.DistrictName
from MPOO_List ml
left join ZIPCodes zc ON ml.AreaID = zc.AreaID AND ml.DistrictID = zc.DistrictID
where ml.DistrictID=34
union
select distinct ml.MPOOID, ml.MPOOName, ml.AreaID, ml.DistrictID, zc.AreaName, zc.DistrictName
from MPOO_List ml
left join ZIPCodes zc ON ml.AreaID = zc.AreaID AND ml.DistrictID = zc.DistrictID
where ml.DistrictID<>34
if I leave out the left joins, it works.
Would I be able to also order by MPOOName within each group?
Update 2
Using AreaID=5 and DistrictID=34 (for a user who has district access), this works. It shows 516 of 625 records, but the records are ordered as desired: all the ones with AreaID 5 and DistrictID 34 are on top.
To include missing records, I tried changing "AND" to "OR" in second WHERE clause. This gives me the correct 625 records, but no longer in order desired.
Same thing happens if I add additional columns to SELECT. i.e. Changing select to SELECT ml.MPOOID, ml.MPOOName, ml.ManagerFirstName, ml.ManagerLastName, ml.Phone, ml.Email, ml.AreaID, ml.DistrictID, ml.StartDate, zc.AreaName, zc.DistrictName. This returns either 29,196 or 35,191 records, depending on whether I use "AND" or "OR" in second WHERE clause (takes 10 seconds to execute)
SELECT DISTINCT ml.MPOOID, ml.MPOOName, ml.AreaID, ml.DistrictID
FROM MPOO_List ml
LEFT JOIN ZIPCodes zc ON ml.AreaID = zc.AreaID AND ml.DistrictID = zc.DistrictID
WHERE ml.AreaID = 5 AND ml.DistrictID = 34
UNION
SELECT DISTINCT ml.MPOOID, ml.MPOOName, ml.AreaID, ml.DistrictID
FROM MPOO_List ml
LEFT JOIN ZIPCodes zc ON ml.AreaID <> zc.AreaID AND ml.DistrictID <> zc.DistrictID
where ml.AreaID <> 5 AND ml.DistrictID <> 34
actual selected columns needed:
SELECT ml.MPOOID, ml.MPOOName, ml.FirstName, ml.LastName, ml.Phone, ml.Email, ml.AreaID, ml.DistrictID, ml.StartDate, zc.AreaName, zc.DistrictName
Update 3 - Sample DB table and data
CREATE TABLE [MPOO_List](
[MPOOID] [int] NOT NULL,
[MPOOName] [varchar](100) NOT NULL,
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
[Phone] [varchar](14) NULL,
[Email] [varchar](100) NULL,
[AreaID] [int] NULL,
[DistrictID] [int] NULL,
[StartDate] [datetime2](0) NULL,
[DefaultMPOO] [bit] NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [MPOO_Zips](
[MPOOID] [int] NOT NULL,
[AreaID] [int] NOT NULL,
[DistrictID] [int] NOT NULL,
[ZipCode] [varchar](5) NOT NULL
) ON [PRIMARY]
GO
insert into MPOO_List values(1000,'PDC','Mary','Jane','(555) 888-7650', 'mary.jane#here.com', 5,23,'2017-03-17 15:49:57',0);
insert into MPOO_List values(1001,'NDC','John','Doe','(555) 888-7621','john.doe#there.com',5,23,'2015-10-01 11:17:08',0);
insert into MPOO_List values(1103,'MPOO 2','Ann','Nonymous','(555) 888-2149','ann.Nonymous#here.com',5,55,'2018-03-23 09:59:06',0);
insert into MPOO_List values(1104,'MPOO 3','Homer','Simpson','(555) 251-0423','homer.simpson#fbi.gov',5,55,'2018-03-23 09:55:50',0);
insert into MPOO_List values(2203,'MPOO 2','Bart','Simpson','(555) 670-6075','bart.simpson#nasa.gov',11,44,'2016-09-20 06:26:43',0);
insert into MPOO_List values(2204,'MPOO 0','Lisa','Simpson','(555) 523-2138','lisa.simpson#mtv.gov',11,44,'2016-09-17 05:45:14',0);
insert into mpoo_zips values (1000,5,23,50265);
insert into mpoo_zips values (1000,5,23,50266);
insert into mpoo_zips values (1000,5,23,50307);
insert into mpoo_zips values (1001,5,23,50533);
insert into mpoo_zips values (1001,5,23,50535);
insert into mpoo_zips values (1001,5,23,50536);
insert into mpoo_zips values (1103,5,55,56341);
insert into mpoo_zips values (1103,5,55,56342);
insert into mpoo_zips values (1103,5,55,56353);
insert into mpoo_zips values (1104,5,55,56450);
insert into mpoo_zips values (1104,5,55,56449);
insert into mpoo_zips values (1104,5,55,56452);
insert into mpoo_zips values (2204,11,4,20006);
insert into mpoo_zips values (2204,11,44,20033);
insert into mpoo_zips values (2204,11,44,20035);
Assuming I want data for AreaID 11, DistrictID 44 to be on top.
You could use UNION to concatenate two queries, the first with the current area and district selection (WHERE AreaID = 5 AND DistrictID = 8), and the second with its opposite (WHERE NOT(AreaID = 5 AND DistrictID = 8).
SELECT /* some columns */ FROM mytable
WHERE AreaID = 5 AND DistrictID = 8
UNION
SELECT /* the same columns */ FROM mytable
WHERE NOT(AreaID = 5 AND DistrictID = 8)
UNIONed queries also seem to be reordered after being united - get around this by selecting an integer (which you will use for your sort order) and giving it a name, and putting all that inside a subquery that orders by the name of your integer.
SELECT * FROM
(
/* UNIONed query goes here */
)
ORDER BY manual_order
There's a catch - you must make sure that the columns you select have unique names. If they don't, the superquery gets confused. You can work around it by aliasing the column names if you need to.
SELECT ml.ID AS mlID, zc.ID as zcID
A user with AreaID of 5 and DistrictID of 0 has edit access to all data within his/her Area which spans multiple districts.
You were right about the problem being in the join, the problem is ml.DistrictID = zc.DistrictID, what should happen if the DistrictID is 0. On reflection, I can't see why you aren't joining on the MPOOID column.
Same thing happens if I add additional columns to SELECT
The UNION result is automatically deduplicated, so if you remove columns that aren't unique, you will find the result set shrinks...
Would I be able to also order by MPOOName within each group?
Just add it as a secondary column in the ORDER BY clause.
ORDER BY manual_order, MPOOName
SQLFiddle complete at http://sqlfiddle.com/#!18/0e442/57
Unrelated, but if you have control of your database design and 5 minutes of spare time, I'd recommend unifying the casing of your table and column names. If your table names are lowercase then use lowercase column names too (and underscores to separate words), else use PascalCase. Everyone's got their preference, for me I prefer the contrast of CAPS for SQL keywords, and lowercase for table/column names.
I'm assuming that your ASP.NET application already knows the AreaID and DistrictID, so you might want to use a builder pattern that checks whether DistrictID > 0, and only include the extra conditions when that returns true.
districtJoin = '';
districtWhere1 = '';
districtWhere2 = '';
if (mlDistrictID > 0)
{
districtJoin = ' AND ml.DistrictID = zc.DistrictID';
districtWhere = ' AND DistrictID = ' + mlDistrictID.ToString();
}
string queryString =
'SELECT /* ml and zc columns */ FROM MPOO_List ml'
+ ' LEFT JOIN ZIPCodes zc ON ml.AreaID = zc.AreaID' + districtJoin
+ ' WHERE ml.AreaID = ' + mlAreaID.ToString() + districtWhere1
+ ' UNION '
+ ' SELECT /* ml and zc columns */ FROM MPOO_List ml'
+ ' LEFT JOIN ZIPCodes zc ON ml.AreaID = zc.AreaID ' + districtJoin
+ ' WHERE NOT(ml.AreaID = ' + mlAreaID.ToString() + districtWhere + ')';
Related
I have a StudentID column, a column on passing 030 class level by a certain date, and a column on passing 040 class level by a certain date.
Can't get the two results on the same line with the ID because these refer to different classes taken at different times (some students completed 030 or 040 in a different semester so there are N / Y and Y / N results for some of the IDs.
Desired outcome: StudentID has unique values, 030 and 040 is either Y or N. All on one line for each StudentID.
Here is a screen shot of my query setup.
Here is a screenshot of the current output. If an ID number has two Ys, I want them to show on the same line.
From your 'Query Setup' screenshot, I can't see the foreign key that you are using with the LEFT JOIN from the StudentID field in your 152_Entrants table, but assuming that this foreign key is also called StudentID, then I would suggest the following (untested) code:
SELECT
[152_Entrants].StudentID,
Nz(P030.PassOrFail,"N") AS 030pass,
Nz(P040.PassOrFail,"N") AS 040pass
FROM
(
152_Entrants
LEFT JOIN
(
SELECT DISTINCT StudentID, PassOrFail FROM FinalMark
WHERE
SemesterNumber <= 162 AND
CourseType = "040" AND
PassOrFail = "P"
) AS P040 ON [152_Entrants].StudentID = P040.StudentID
)
LEFT JOIN
(
SELECT DISTINCT StudentID, PassOrFail FROM FinalMark
WHERE
SemesterNumber <= 162 AND
CourseType = "030" AND
PassOrFail = "P"
) AS P030 ON [152_Entrants].StudentID = P030.StudentID;
Here, each of the subqueries is returning the unique set of StudentID values for which PassOrFail is "P" for records with SemesterNumber <= 162 for each CourseType.
Your table 152_Extrants is then LEFT JOINed to each subquery such that if a matching StudentID is found in the subquery, the student has passed (based on your criteria), else the student has failed and so the Nz function will return "N".
EDIT
On viewing the composition of your 152_Entrants query, the complete query could become:
SELECT
E152.StudentID,
Nz(P030.PassOrFail,"N") AS 030pass,
Nz(P040.PassOrFail,"N") AS 040pass
FROM
(
(
SELECT StudentID FROM FinalMark
WHERE SemesterNumber = 152
GROUP BY StudentID
) AS E152
LEFT JOIN
(
SELECT DISTINCT StudentID, PassOrFail FROM FinalMark
WHERE
SemesterNumber <= 162 AND
CourseType = "040" AND
PassOrFail = "P"
) AS P040 ON E152.StudentID = P040.StudentID
)
LEFT JOIN
(
SELECT DISTINCT StudentID, PassOrFail FROM FinalMark
WHERE
SemesterNumber <= 162 AND
CourseType = "030" AND
PassOrFail = "P"
) AS P030 ON E152.StudentID = P030.StudentID;
Without the need for a separate 152_Entrants query.
Write a SQL statement which can generate the list of customers whose minutes Streamed is consistently less than the previous minutes Streamed. As in minutes Streamed in the nth order is less than minutes Streamed in n-1th order, and the next previous order is also less. Another way to say it, list the customers that watch less and less minutes each time they watch a movie.
The table, query:
sqlfiddle link:
I have come up with the following query:
select distinct c1.customer_Id
from Customer c1
join Customer c2
where c1.customer_Id = c2.customer_Id
and c1.purchaseDate > c2.purchaseDate
and c1.minutesStreamed < c2.minutesStreamed;
This query doesn't deal with the (n-1)st and (n-2)nd comparison, i.e. "minutes Streamed in the nth order is less than minutes Streamed in n-1th order, and the next previous order is also less." condition.
I have attached a link for sqlfiddle, where I have created the table.
Hello Continuous Learner,
the following statement works for the n-1 and n-2 relation.
select distinct c1.customer_Id
from Customer c1
join Customer c2
on c1.customer_Id = c2.customer_Id
join Customer c3
on c1.customer_Id = c3.customer_Id
where c1.purchaseDate < c2.purchaseDate
and c1.minutesStreamed > c2.minutesStreamed
and c2.purchaseDate < c3.purchaseDate
and c2.minutesStreamed > c3.minutesStreamed
Although, I currently don't have an automatic solution for this problem.
Cheers
I would use a ROW_NUMBER() function with partition by customer id.
and then do a self join, on customer id and rank = rank-1, to bring new and old at the same level
Like:
create temp_rank_table as
(
select
customer_Id,
purchaseDate ,
minutesStreamed,
ROW_NUMBER() OVER (PARTITION BY customer_Id, ORDER BY purchaseDate, minutesStreamed) as cust_row
from Customer
)
self join
select customer_Id
( select
newval.customer_Id,
sum(case when newval.minutesStreamed < oldval.minutesStreamed then 1 else 0 end) as LessThanPrevCount,
max(newval.cust_row) as totalStreamCount
from temp_rank_table newval
left join temp_rank_table oldval
on newval.customer_id = oldval.customer_id
and newval.cust_row-1 = oldval.cust_row -- cust_row 2 matches to cust_row 1
group by newval.customer_id
)A
where A.LessThanPrevCount = (A.totalStreamCount-1)
-- get customers who always stream lesser than previous
--you can use having clause instead of a subquery too
DECLARE #TBL AS TABLE ( [NO] INT, [CODE] VARCHAR(50), [AREA]
VARCHAR(50) )
/* EXAMPLE 1 */ INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(1,'001','A00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(2,'001','A00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(3,'001','B00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(4,'001','C00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(5,'001','C00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(6,'001','A00') INSERT INTO #TBL([NO],[CODE],[AREA]) VALUES
(7,'001','A00')
/* EXAMPLE 2 / / ***** USE THIS CODE TO ENTER DATA FROM DIRECT TABLE
***** SELECT ROW_NUMBER() OVER(ORDER BY [FIELD_DATE]) AS [NO] ,[FIELD_CODE] AS [CODE] ,[FIELD_AREA] AS [AREA] FROM TABLE_A WHERE
CAST([FIELD_DATE] AS DATE) >= CAST('20200307' AS DATE) ORDER BY
[FIELD_DATE],[FIELD_CODE]
*/
SELECT A.NO AS ANO ,A.CODE AS ACODE ,A.AREA AS AAREA ,B.NO AS BNO
,B.CODE AS BCODE ,B.AREA AS BAREA ,CASE WHEN A.AREA=B.AREA THEN
'EQUAL' ELSE 'NOT EQUAL' END AS [COMPARE AREA] FROM #TBL A LEFT JOIN
#TBL B ON A.NO=B.NO+1
Blockquote
SELECT *
FROM `eBayorders`
WHERE (`OrderIDAmazon` IS NULL
OR `OrderIDAmazon` = "null")
AND `Flag` = "True"
AND `TYPE` = "GROUP"
AND (`Carrier` IS NULL
OR `Carrier` = "null")
AND LEFT(`SKU`, 1) = "B"
AND datediff(now(), `TIME`) < 4
AND (`TrackingInfo` IS NULL
OR `TrackingInfo` = "null")
AND `STATUS` = "PROCESSING"
GROUP BY `Name`,
`SKU`
ORDER BY `TIME` ASC LIMIT 7
I am trying to make sure that none of the names and skus will show up in the same result. I am trying to group by name and then sku, however I ran into the problem where a result showed up that has the same name and different skus, which I dont want to happen. How can I fix this query to make sure that there is always distinct names and skus in the result set?!
For example say I have an Order:
Name: Ben Z, SKU : B000334, oldest
Name: Ben Z, SKU : B000333, second oldest
Name: Will, SKU: B000334, third oldest
Name: John, SKU: B000036, fourth oldest
The query should return only:
Name: Ben Z, SKU : B000334, oldest
Name: John, SKU: B000036, fourth oldest
This is because all of the Names should only have one entry in the set along with SKU.
There are two problems here.
The first is the ANSI standard says that if you have a GROUP BY clause, the only things you can put in the SELECT clause are items listed in GROUP BY or items that use an aggregate function (SUM, COUNT, MAX, etc). The query in your question selects all the columns in the table, even those not in the GROUP BY. If you have multiple records that match a group, the table doesn't know which record to use for those extra columns.
MySql is dumb about this. A sane database server would throw an error and refuse to run that query. Sql Server, Oracle and Postgresql will all do that. MySql will make a guess about which data you want. It's not usually a good idea to let your DB server make guesses about data.
But that doesn't explain the duplicates... just why the bad query runs at all. The reason you have duplicates is that you group on both Name and SKU. So, for example, for Ben Z's record you want to see just the oldest SKU. But when you group on both Name and SKU, you get a seperate group for { Ben Z, B000334 } and { Ben Z, B000333 }... that's two rows for Ben Z, but it's what the query asked for, since SKU is also part of what determines a group.
If you only want to see one record per person, you need to group by just the person fields. This may mean building that part of the query first, to determine the base record set you need, and then JOINing to this original query as part of your full solution.
SELECT T1.*
FROM eBayorders T1
JOIN
( SELECT `Name`,
`SKU`,
max(`TIME`) AS MAX_TIME
FROM eBayorders
WHERE (`OrderIDAmazon` IS NULL OR `OrderIDAmazon` = "null") AND `Flag` = "True" AND `TYPE` = "GROUP" AND (`Carrier` IS NULL OR `Carrier` = "null") AND LEFT(`SKU`, 1) = "B" AND datediff(now(), `TIME`) < 4 AND (`TrackingInfo` IS NULL OR `TrackingInfo` = "null") AND `STATUS` = "PROCESSING"
GROUP BY `Name`,
`SKU`) AS dedupe ON T1.`Name` = dedupe.`Name`
AND T1.`SKU` = dedupe.`SKU`
AND T1.`Time` = dedupe.`MAX_TIME`
ORDER BY `TIME` ASC LIMIT 7
Your database platform should have complained because your original query had items in the select list which were not present in the group by (generally not allowed). The above should resolve it.
An even better option would be the following if your database supported window functions (MySQL doesn't, unfortunately):
SELECT *
FROM
( SELECT *,
row_number() over (partition BY `Name`, `SKU`
ORDER BY `TIME` ASC) AS dedupe_rank
FROM eBayorders
WHERE (`OrderIDAmazon` IS NULL OR `OrderIDAmazon` = "null") AND `Flag` = "True" AND `TYPE` = "GROUP" AND (`Carrier` IS NULL OR `Carrier` = "null") AND LEFT(`SKU`, 1) = "B" AND datediff(now(), `TIME`) < 4 AND (`TrackingInfo` IS NULL OR `TrackingInfo` = "null") AND `STATUS` = "PROCESSING" ) T
WHERE dedupe_rank = 1
ORDER BY T.`TIME` ASC LIMIT 7
You are trying to obtain a result set which doesn't have repeats in either the SKU nor the Name column.
You might have to add a subquery to your query, to accomplish that. The inner query would group by Name, and the Outer query would group by SKU, such that you won't have repeats in either column.
Try this :
SELECT *
FROM
(SELECT *
FROM eBayorders
WHERE (`OrderIDAmazon` IS NULL
OR `OrderIDAmazon` = "null")
AND `Flag` = "True"
AND `TYPE` = "GROUP"
AND (`Carrier` IS NULL
OR `Carrier` = "null")
AND LEFT(`SKU`, 1) = "B"
AND datediff(now(), `TIME`) < 4
AND (`TrackingInfo` IS NULL
OR `TrackingInfo` = "null")
AND `STATUS` = "PROCESSING"
GROUP BY Name)
GROUP BY `SKU`
ORDER BY `TIME` ASC LIMIT 7
With this approach you just filter out rows that do not contain the largest/latest value for TIME.
SELECT SKU, Name
FROM eBayOrders o
WHERE NOT EXISTS (SELECT 0 FROM eBayOrders WHERE Name = o.name and Time > o.Time)
GROUP BY SKU, Name
Note: If two records have exactly the same Name and Time values, you may still end up getting duplicates, because the logic you have specified does not provide any way to break up a tie.
I have a table that contains two columns
ID | Name
----------------
1 | John
2 | Sam
3 | Peter
6 | Mike
It has missed IDs. In this case these are 4 and 5.
How do I find and insert them together with random names into this table?
Update: cursors and temp tables are not allowed. The random name should be 'Name_'+ some random number. Maybe it would be the specified value like 'Abby'. So it doesn't matter.
Using a recursive CTE you can determine the missing IDs as follows
DECLARE #Table TABLE(
ID INT,
Name VARCHAR(10)
)
INSERT INTO #Table VALUES (1, 'John'),(2, 'Sam'),(3,'Peter'),(6, 'Mike')
DECLARE #StartID INT,
#EndID INT
SELECT #StartID = MIN(ID),
#EndID = MAX(ID)
FROM #Table
;WITH IDS AS (
SELECT #StartID IDEntry
UNION ALL
SELECT IDEntry + 1
FROM IDS
WHERE IDEntry + 1 <= #EndID
)
SELECT IDS.IDEntry [ID]
FROM IDS LEFT JOIN
#Table t ON IDS.IDEntry = t.ID
WHERE t.ID IS NULL
OPTION (MAXRECURSION 0)
The option MAXRECURSION 0 will allow the code to avoid the recursion limit of SQL SERVER
From Query Hints and WITH common_table_expression (Transact-SQL)
MAXRECURSION number Specifies the maximum number of recursions
allowed for this query. number is a nonnegative integer between 0 and
32767. When 0 is specified, no limit is applied. If this option is not specified, the default limit for the server is 100.
When the specified or default number for MAXRECURSION limit is reached
during query execution, the query is ended and an error is returned.
Because of this error, all effects of the statement are rolled back.
If the statement is a SELECT statement, partial results or no results
may be returned. Any partial results returned may not include all rows
on recursion levels beyond the specified maximum recursion level.
Generating the RANDOM names will largly be affected by the requirements of such a name, and the column type of such a name. What exactly does this random name entail?
You can do this using a recursive Common Table Expression CTE. Here's an example how:
DECLARE #MaxId INT
SELECT #MaxId = MAX(ID) from MyTable
;WITH Numbers(Number) AS
(
SELECT 1
UNION ALL
SELECT Number + 1 FROM Numbers WHERE Number < #MaxId
)
SELECT n.Number, 'Random Name'
FROM Numbers n
LEFT OUTER JOIN MyTable t ON n.Number=t.ID
WHERE t.ID IS NULL
Here are a couple of articles about CTEs that will be helpful to Using Common Table Expressions and Recursive Queries Using Common Table Expressions
Start by selecting the highest number in the table (select top 1 id desc), or select max(id), then run a while loop to iterate from 1...max.
See this article about looping.
For each iteration, see if the row exists, and if not, insert into table, with that ID.
I think recursive CTE is a better solution, because it's going to be faster, but here is what worked for me:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestTable]') AND type in (N'U'))
DROP TABLE [dbo].[TestTable]
GO
CREATE TABLE [dbo].[TestTable](
[Id] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
GO
INSERT INTO [dbo].[TestTable]([Id],[Name]) VALUES (1, 'John')
INSERT INTO [dbo].[TestTable]([Id],[Name]) VALUES (2, 'Sam')
INSERT INTO [dbo].[TestTable]([Id],[Name]) VALUES (3, 'Peter')
INSERT INTO [dbo].[TestTable]([Id],[Name]) VALUES (6, 'Mike')
GO
declare #mod int
select #mod = MAX(number)+1 from master..spt_values where [type] = 'P'
INSERT INTO [dbo].[TestTable]
SELECT y.Id,'Name_' + cast(newid() as varchar(45)) Name from
(
SELECT TOP (select MAX(Id) from [dbo].[TestTable]) x.Id from
(
SELECT
t1.number*#mod + t2.number Id
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
WHERE t1.[type] = 'P' and t2.[type] = 'P'
) x
WHERE x.Id > 0
ORDER BY x.Id
) y
LEFT JOIN [dbo].[TestTable] on [TestTable].Id = y.Id
where [TestTable].Id IS NULL
GO
select * from [dbo].[TestTable]
order by Id
GO
http://www.sqlfiddle.com/#!3/46c7b/18
It's actually very simple :
Create a table called #All_numbers which should contain all the natural number in the range that you are looking for.
#list is a table containing your data
select a.num as missing_number ,
'Random_Name' + convert(varchar, a.num)
from #All_numbers a left outer join #list l on a.num = l.Id
where l.id is null
I have a table of postcodes and I want to update each postcode with its 3 nearest neighbours. Ie to fill in the blanks in this table:
postcode nearestPostcode1 nearestPostcode2 nearestPostcode3
_______________________________________________________________
KY6 1DA - - -
KY6 1DG - - -
KY6 2DT - - -
KY6 1RG - - -
....
I've figured out a SELECT query to find the nearest postcodes and here is one clumsy way the first row could be updated:
update table1 set
nearestPostcode1 = (select query for returning the first nearest postcode),
nearestPostcode2 = (select query for returning the second nearest postcode),
nearestPostcode3 = (select query for returning the third nearest postcode)
where postcode = 'KY6 1DA';
However this will result in 3 select queries being run for each row update. It would be more efficient if there was some way to do what is expressed by this pseudo code:
update table1 set
(nearestPostcode1, nearestPostcode2, nearestPostcode3) =
(select query to return the 3 nearest postcodes)
where postcode = 'KY6 1DA';
The 'select query' in the above looks like this:
select postcode from postcodeTable
order by <equation to calculate distance> ASC
limit 3
Is there anyway for the rows returned from the select to be put into a form that they can be used to update multiple fields?
Thanks.
Update Table1
Cross Join (
Select Min( Case When Z1.Num = 1 Then Z1.postcode End ) As PostCode1
, Min( Case When Z1.Num = 2 Then Z1.postcode End ) As PostCode2
, Min( Case When Z1.Num = 3 Then Z1.postcode End ) As PostCode3
From (
Select postcode
, #num := #num + 1 As Num
From postcodeTable
Where postcode = 'KY6 IDA'
Order By <equation to calculate distance> ASC
Limit 3
) As Z1
) As Z
Set nearestPostCode1 = Z.PostCode1
, nearestPostCode2 = Z.PostCode2
, nearestPostCode3 = Z.PostCode3
Where Table1.postcode = 'KY6 IDA'
You can do something similar to this:
UPDATE table1
SET
nearestPostcode1 = pc1,
nearestPostcode2 = pc2,
nearestPostcode3 = pc3
FROM
(SELECT pc1, pc2, pc3 FROM ....) t
WHERE
postcode = 'KY6 1DA';
I found this related question on Stackoverflow on how to transform columns to rows:
MySQL - Rows to Columns
In your case, you can do something like
SELECT
IF(#rownum=1,postcode,'') ) AS pc1,
IF(#rownum=2,postcode,'') ) AS pc2,
IF(#rownum=3,postcode,'') ) AS pc2,
FROM
(SELECT postcode
FROM postcodeTable
ORDER BY <equation to calculate distance> ASC
LIMIT 3)
Here is a hack to simulate the ROW_NUMBER() functionality in MySQL [1]:
SELECT #rownum:=#rownum+1 rownum, t.*
FROM (SELECT #rownum:=0) r, mytable t;
I think you could do this with the pseudo-code:
REPLACE INTO table1 (postcode, nearestPostcode1, nearestPostcode2, nearestPostcode3)
SELECT "KY6 1DA", col1, col2, col3 FROM myTable WHERE ...;
it'd be easier to specify it seeing the real SQL.
Note the first column is specified as a constant in quotes. For this to work postcode must be a UNIQUE or PRIMARY index.
Anytime I see a table with columns that have 1-up counters after their names, I get concerned.
In general, it is a Bad Idea (TM) to store data that can be calculated from data that is already stored. What happens if your application all of a sudden needs the 4 closest postal codes? What if the postal code boundaries change?
Assuming the distance calculation isn't very complex, you'll be better off in the long run not explicitly storing this data.