Используйте Access SQL для группового ранжирования - PullRequest
9 голосов
/ 16 декабря 2010

Как мне распределить продавцов по # клиентам , сгруппированным по отделам (включая связи)?

Например, учитывая эту таблицу, я хочу создать столбец Ранг справа. Как мне это сделать в Access?

SalesPerson Dept #Customers Rank
Bill        DeptA     20    1
Ted         DeptA     30    2
Jane        DeptA     40    3
Bill        DeptB     50    1
Mary        DeptB     60    2

Я уже знаю, как сделать простое ранжирование с помощью этого кода SQL. Но я не знаю, как переработать это, чтобы принять группировку.

Select Count(*) from [Tbl] Where [#Customers] <  [Tblx]![#Customers] )+1

Кроме того, есть множество ответов для этого с помощью функции Rank () SQL Server, но мне нужно сделать это в Access. Предложения, пожалуйста?

Ответы [ 6 ]

13 голосов
/ 16 декабря 2010
SELECT *, (select count(*) from tbl as tbl2 where
tbl.customers > tbl2.customers and tbl.dept = tbl2.dept) + 1 as rank from tbl

Просто добавьте поле dept в подзапрос ...

3 голосов
/ 26 июля 2013

Отличное решение с подзапросом! За исключением огромных наборов записей, решение подзапроса становится очень медленным. Лучше (быстрее) использовать Self JOIN, посмотрите следующее решение: self join

SELECT tbl1.SalesPerson , count(*) AS Rank 
FROM tbl AS tbl1 INNER JOIN tbl AS tbl2 ON tbl1.DEPT = tbl2.DEPT 
    AND tbl1.#Customers < tbl2.#Customers 
GROUP BY tbl1.SalesPerson 
2 голосов
/ 09 октября 2014

Я знаю, что это старая тема.Но так как я потратил много времени на очень похожую проблему, и мне очень помогли предыдущие ответы, приведенные здесь, я хотел бы поделиться тем, что оказалось НАМНОГО более быстрым способом.(Осторожно, это сложнее.)

Сначала создайте еще одну таблицу под названием «Индивидуализатор».Это будет одно поле, содержащее список чисел от 1 до наивысшего ранга, который вам нужен.

Затем создайте модуль VBA и вставьте в него следующее:

'Global Declarations Section.
Option Explicit
Global Cntr

'*************************************************************
' Function:  Qcntr()
'
' Purpose: This function will increment and return a dynamic
' counter. This function should be called from a query.
'*************************************************************

Function QCntr(x) As Long
   Cntr = Cntr + 1
   QCntr = Cntr
End Function

'**************************************************************
' Function:  SetToZero()
'
' Purpose: This function will reset the global Cntr to 0. This
' function should be called each time before running a query
' containing the Qcntr() function.
'**************************************************************

Function SetToZero()
   Cntr = 0
End Function

Сохраните его как Module1.

Далее создайте Query1 следующим образом:

SELECT Table1.Dept, Count(Table1.Salesperson) AS CountOfSalesperson
FROM Table1
GROUP BY Table1.Dept;

Создайте запрос MakeTable с именем Query2 следующим образом:

SELECT SetToZero() AS Expr1, QCntr([ID]) AS Rank, Query1.Dept, 
Query1.CountOfSalesperson, Individualizer.ID 
INTO Qtable1
FROM Query1 
INNER JOIN Individualizer 
   ON Query1.CountOfSalesperson >= Individualizer.ID;

Создайте еще один запрос MakeTable с именемQuery3, например, так:

SELECT SetToZero() AS Expr1, QCntr([Identifier]) AS Rank, 
[Salesperson] & [Dept] & [#Customers] AS Identifier, Table1.Salesperson, 
Table1.Dept, Table1.[#Customers] 
INTO Qtable2
FROM Table1;

Если у вас уже есть другое поле, уникально идентифицирующее каждую строку, вам не нужно создавать поле идентификатора.

Запустите Query2 и Query3 для создания таблиц.Создайте четвертый запрос с именем Query4, например:

SELECT Qtable2.Salesperson, Qtable2.Dept, Qtable2.[#Customers], Qtable1.ID AS Rank
FROM Qtable1 
INNER JOIN Qtable2 ON Qtable1.Rank = Qtable2.Rank;

Query4 возвращает искомый результат.

На практике вам нужно написать функцию VBA для запуска Query2 и Query3, а затемвызывайте эту функцию с помощью кнопки, расположенной в удобном месте.

Теперь я знаю, что это звучит до смешного сложного для приведенного вами примера.Но в реальной жизни я уверен, что ваш стол сложнее, чем этот.Надеюсь, мои примеры могут быть применены к вашей реальной ситуации.В моей базе данных с более чем 12 000 записей этот метод является самым быстрым по сравнению с FAR (например, за 6 секунд с 12 000 записей по сравнению с более чем 1 минутой с 262 записями, ранжированными с помощью метода подзапроса).

Настоящий секрет для меня былзапрос MakeTable, потому что этот метод ранжирования бесполезен, если вы сразу не выводите результаты в таблицу.Но это ограничивает ситуации, к которым это может быть применено.

PS Я забыл упомянуть, что в моей базе данных я не получал результаты непосредственно из таблицы.Записи уже прошли ряд запросов и несколько вычислений, прежде чем их нужно было ранжировать.Это, вероятно, во многом способствовало огромной разнице в скорости между двумя методами в моей ситуации.Если вы извлекаете записи непосредственно из таблицы, вы можете не заметить такого улучшения.

0 голосов
/ 28 марта 2017

Я обычно выбираю советы и идеи отсюда, а иногда получаю из этого удивительные вещи!

Сегодня (ну, скажем, в течение прошлой недели) я возился с ранжированием данных в Access и, насколько мог, не ожидал, что собираюсь сделать что-то настолько сложное, что мне неделю, чтобы понять это! Я выбрал сиськи с двух основных сайтов:

  1. https://usefulgyaan.wordpress.com/2013/04/23/ranking-in-ms-access/ (видел ту умную часть «> =», и самость присоединяется? Удивительно… это помогло мне построить мое решение только из одного запроса, в отличие от сложного метода, предложенного выше asonoftheMighty (не дискредитирую вас… просто пока не хочу пробовать, может быть, когда я получу большие данные, я тоже захочу попробовать…)
  2. Прямо здесь, от Пола Эботта (Paul and Abott) ('и tbl.dept = tbl2.dept')… Я потерял после ранжирования, потому что я ставил AND YearID = 1 и т. Д., Тогда ранжирование в конечном итоге происходило только для устанавливает, как вы уже догадались, когда YearID = 1! Но у меня было много разных сценариев ...

Ну, я частично рассказал эту историю, чтобы поблагодарить упомянутых авторов, потому что то, что я сделал, является для меня одним из самых сложных в рейтинге, который, я думаю, может помочь вам практически в любой ситуации, и, поскольку я получил пользу от других, я Я хотел бы поделиться здесь тем, что, я надеюсь, может принести пользу и другим.

Простите, что я не могу публиковать здесь свои структуры таблиц, много связанных таблиц. Я опубликую только запрос, поэтому, если вам нужно, вы можете разработать свои таблицы, чтобы в итоге получить такой запрос. Но вот мой сценарий:

У вас есть ученики в школе. Они проходят классы с 1 по 4, могут быть либо в потоке A или B, либо ни в одном, если класс слишком мал. Каждый из них сдает 4 экзамена (эта часть сейчас не важна), поэтому вы получаете общий балл по моему делу. Это оно. Да ??

Хорошо. Давайте оценим их следующим образом:

Мы хотим знать рейтинг

• все ученики, которые когда-либо проходили эту школу (лучшие ученики)

• все студенты в определенном учебном году (студент года)

• учащиеся определенного класса (но помните, что учащийся прошел все классы, так что, в основном, его / ее звание в каждом из этих классов за разные годы) это обычный рейтинг, который указывается в табелях

• учащиеся в своих потоках (применяется вышеуказанный комментарий)

• Я также хотел бы знать, к какой группе населения мы причислили этого студента в каждой категории

... все в одной таблице / запросе. Теперь вы поняли?

(Обычно я люблю выполнять столько «программирования» в базе данных / запросах, чтобы получить визуальные эффекты и уменьшить объем кода, который мне потом придется исправить. На самом деле я не буду использовать этот запрос в своем приложении :), но он дал мне знать, где и как отправить мои параметры в запрос, из которого он получен, и каких результатов ожидать в моем rdlc)

Не волнуйтесь, вот оно:

SELECT Sc.StudentID, Sc.StudentName, Sc.Mark, 
(SELECT COUNT(Sch.Mark) FROM [StudentScoreRankTermQ] AS Sch WHERE (Sch.Mark >= Sc.Mark)) AS SchoolRank, 
(SELECT Count(s.StudentID) FROM StudentScoreRankTermQ AS s) As SchoolTotal, 
(SELECT COUNT(Yr.Mark) FROM [StudentScoreRankTermQ] AS Yr WHERE (Yr.Mark >= Sc.Mark) AND (Yr.YearID = Sc.YearID) ) AS YearRank, 
(SELECT COUNT(StudentID) FROM StudentScoreRankTermQ AS Yt WHERE (Yt.YearID = Sc.YearID) ) AS YearTotal, 
(SELECT COUNT(Cl.Mark) FROM [StudentScoreRankTermQ] AS Cl WHERE (Cl.Mark >= Sc.Mark) AND (Cl.YearID = Sc.YearID) AND (Cl.TermID = Sc.TermID)  AND (Cl.ClassID=Sc.ClassID)) AS ClassRank, 
(SELECT COUNT(StudentID) FROM StudentScoreRankTermQ AS C WHERE (C.YearID = Sc.YearID) AND (C.TermID = Sc.TermID) AND (C.ClassID = Sc.ClassID) ) AS ClassTotal, 
(SELECT COUNT(Str.Mark) FROM [StudentScoreRankTermQ] AS Str WHERE (Str.Mark >= Sc.Mark)  AND (Str.YearID = Sc.YearID) AND (Str.TermID = Sc.TermID)  AND (Str.ClassID=Sc.ClassID) AND (Str.StreamID = Sc.StreamID) ) AS StreamRank, 
(SELECT COUNT(StudentID) FROM StudentScoreRankTermQ AS St WHERE (St.YearID = Sc.YearID) AND (St.TermID = Sc.TermID)  AND (St.ClassID = Sc.ClassID) AND (St.StreamID = Sc.StreamID) ) AS StreamTotal, 
Sc.CalendarYear, Sc.Term, Sc.ClassNo, Sc.Stream, Sc.StreamID, Sc.YearID, Sc.TermID, Sc.ClassID
FROM StudentScoreRankTermQ AS Sc
ORDER BY Sc.Mark DESC;

Вы должны получить что-то вроде этого:

StudentID | StudentName | Марк | SchoolRank | SchoolTotal | YearRank | YearTotal | ClassRank | ClassTotal | StreamRank | StreamTotal | Год | Срок | Класс | Поток

1 | Джейн | 200 | 1 | 20 | 2 | 12 | 1 | 9 | 1 | 5 | 2017 | I | 2 | A

2 | Том | 199 | 2 | 20 | 1 | 12 | 3 | 9 | 1 | 4 | 2016 | I | 1 | B

Используйте разделители (|) для восстановления таблицы результатов

Просто представление о таблицах, каждый ученик будет связан с классом. Каждый класс относится к годам. Каждый поток относится к классу. Каждый термин относится к году. Каждый экзамен относится к семестру и студенту, классу и году; студент может быть в классе 1А в 2016 году и перейти в класс 2b в 2017 году и т. д. *

Позвольте мне также добавить, что это бетаВ результате я недостаточно хорошо его протестировал, и у меня пока нет возможности создать много данных, чтобы увидеть производительность.Мой первый взгляд на это сказал мне, что это хорошо.Поэтому, если вы найдете причины или предупреждения, которые хотите указать мне, пожалуйста, сделайте это в комментариях, чтобы я мог продолжать учиться!

0 голосов
/ 01 февраля 2013

Чтобы добавить к этому и любым другим связанным с Access ранжам или пошаговым инструкциям Rank Tie Breaker для других версий Access, ранжирование не должно выполняться по запросам кросс-таблиц, если в предложении FROM НЕ содержится таблица, а есть запрос, который либозапрос кросс-таблицы или запрос, который содержит внутри него в другом месте запрос кросс-таблицы.

Приведенный выше код, где используется оператор SELECT в статистике SELECT (подзапрос),

 "SELECT *, (select count(*) from tbl as tbl2 where tbl.customers > tbl2.customers and tbl.dept = tbl2.dept) + 1 as rank from tbl"

будетне работает и всегда будет не отображать ошибку в части кода, где "tbl.customers> tbl2.customers " не может быть найден.

В моей ситуации наВ прошлом проекте я ссылался на запрос, а не на таблицу, и в этом запросе я ссылался на кросс-таблицу, что приводило к сбою и возникновению ошибки.Я смог решить эту проблему, сначала создав таблицу из запроса кросс-таблицы, и когда я сослался на вновь созданную таблицу в предложении FROM, она начала работать для меня.

Так что в конечном итоге обычно вы можете ссылаться назапрос или таблица в предложении FROM оператора SELECT, как было указано выше для ранжирования, но будьте осторожны, если вы ссылаетесь на запрос вместо таблицы, этот запрос должен Not быть кросс-таблицейзапросить или ссылаться на другой запрос, который является кросс-таблицей.

Надеюсь, что это поможет всем, у кого могли возникнуть проблемы с поиском возможной причины, если вам случается ссылаться на приведенные выше операторы, и вы не ссылаетесь на таблицу в вашем FROMпункт в вашем собственном проекте.Кроме того, выполнение подзапросов для псевдонимов с запросами кросс-таблиц в Access, вероятно, не является хорошей идеей или наилучшей практикой, так что избегайте этого, если / когда это возможно.

Если вы сочли это полезным и хотели бы, чтобы Access позволилиспользуйте прокручивающуюся мышь в промежуточном редакторе запросов, подскажите, пожалуйста.

0 голосов
/ 16 декабря 2010

Тебе нужно заняться математикой.Обычно я использую комбинацию поля счетчика и поля смещения.Вы стремитесь к таблице, которая выглядит следующим образом (#Customers не является необходимым, но даст вам визуальное представление о том, что вы делаете это правильно):

SalesPerson Dept #Customers Ctr Offset
Bill        DeptA     20    1   1
Ted         DeptA     30    2   1
Jane        DeptA     40    3   1
Bill        DeptB     50    4   4
Mary        DeptB     60    5   4

Итак, чтобы присвоить ранг, вы'd do [Ctr] - [Offset] +1 AS Rank

  1. создать таблицу с SalesPerson, Dept, Ctr и Offset
  2. вставкойв эту таблицу, упорядоченные по Dept и #Customers (чтобы все они были правильно отсортированы)
  3. Обновите Offset до MIN(Ctr), группируя по Dept
  4. Выполните математические вычисления, чтобы определить Rank
  5. Очистите таблицу, чтобы вы могли использовать ее снова в следующий раз.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...