Я давно читал следующее решение о SO, но не могу найти ссылку для кредита, но здесь идет:
SELECT users.*, payments.method, payments.id AS payment_id, payments2.id
FROM users
JOIN payments
ON users.id = payments.user_id
LEFT JOIN payments2
ON payments.user_id = payments2.user_id
AND payments.id < payments2.id
WHERE payments2.id IS NULL
Чтобы понять, как это работает, просто снимите WHERE payments2.id IS NULL
и вы увидите, что происходит, например, он может произвести следующий вывод (я не строю схему для проверки этого, так что это псевдовыход). Предположим, что в payments
имеются следующие записи:
id | user_id | method
1 | 1 | VISA
2 | 1 | VISA
3 | 1 | VISA
4 | 1 | VISA
И вышеупомянутый SQL (без предложения WHERE payments2.id IS NULL
) должен произвести:
users.id | payments.method | payments.id | payments2.id
1 | VISA | 1 | 2
1 | VISA | 1 | 3
1 | VISA | 1 | 4
1 | VISA | 2 | 3
1 | VISA | 2 | 4
1 | VISA | 3 | 4
1 | VISA | 4 | NULL
Как вы можете видеть последняя строка дает желаемый результат, а поскольку нет payments2.id > 4
, LEFT JOIN приводит к payments2.id = NULL
.
Я нашел это решение намного быстрее (из моих ранних тестов) чем принятый ответ.
Используя другую схему, но похожий запрос, из 16095 записей:
select as1.*, as2.id
from allocation_status as1
left join allocation_status as2
on as1.allocation_id = as2.allocation_id
and as1.id < as2.id
where as2.id is null;
16095 rows affected, taking 4.1ms
По сравнению с принятым ответом MAX / подзапроса:
SELECT as1.*
FROM allocation_status as1
JOIN (
SELECT max(id) as id
FROM allocation_status
group by allocation_id
) as_max on as1.id = as_max.id
16095 rows affected, taking 14.8ms
Почему не перегружают getEmployeeName (??) метод?
getEmployeeName (международный BatchID)
getEmployeeName (возражают SSN) (плохая идея)
getEmployeeName (Строковая электронная почта)
и т.д.
Кажется пользой, к которой 'многие' приближаются мне.
Вы думаете C/C++.
Используйте объекты вместо байта идентификатора (или интервал).
Мое Плохое, подход перегрузки лучше и использует SSN, поскольку первичный ключ не так хорош
public ??? getEmployeeName(Object obj){
if (obj instanceof Integer){
...
} else if (obj instanceof String){
...
} else if .... // and so on
} else throw SomeMeaningFullRuntimeException()
return employeeName
}
Я думаю, что лучше использовать Исключения Непроверенные для передачи сигналов о неправильном входе.
Зарегистрируйте его так, клиент знает что объекты ожидать. Или создайте свои собственные обертки. Я предпочитаю первую опцию.
засуньте все свои опции в перечисление, чего-то как следующее
GetEmployeeName(Enum identifier)
{
switch (identifier)
case eBatchID:
{
// Do stuff
}
case eSSN:
{
}
case eEmailId:
{
}
case eSalary:
{
}
default:
{
// No match
return 0;
}
}
enum Identifier
{
eBatchID,
eSSN,
eEmailID,
eSalary
}
Первое является, вероятно, лучшим в Java, полагая, что это безопасно с точки зрения типов (в отличие от другого). Кроме того, для "нормальных" типов, второе решение, кажется, только предоставляет громоздкое использование пользователю. Однако, так как Вы используете Объект в качестве типа для SSN (который имеет семантическое значение вне Объекта), Вам, вероятно, не сойдет с рук тот тип API.
В целом, в данном случае я использовал бы подход со многими методами считывания. Если все идентификаторы имеют свой собственный тип класса, я, возможно, пошел вторым путем, но переключающийся внутренне на классе вместо provided/application-defined идентификатора типа.
Отделение между поисковым процессом и критериями поиска jrudolf делает предложение в его примере, превосходно. Интересно, почему это не наиболее проголосовавшее решение. Я пропускаю что-то?
Я пошел бы с Объектами Запроса. Они работают хорошо на доступ к таблицам непосредственно. Если Вы ограничены хранимыми процедурами, они теряют часть своего питания, но можно все еще заставить его работать.
Я соглашаюсь с Stephan: Одна задача, одно имя метода, даже если можно сделать это несколько путей. Функция перегрузки метода была обеспечена точно для Вашего случая.
И избегайте своего второго решения по всей стоимости. Это пахнет как "ваш olde пусто * C". Аналогично, передача Java "Объект" почти как плохой стиль как C "пусто *".
Я лично предпочитаю иметь явное именование "... ByRoomNumber", потому что, если Вы заканчиваете со многими "перегрузками", Вы в конечном счете представите нежелательные ошибки. Быть явным является, по моему скромному мнению, лучшим способом.
Если у Вас есть хороший дизайн, необходимо смочь определить, можно ли использовать перегружающийся подход или если Вы собираетесь столкнуться с проблемой, куда, если Вы перегружаетесь, Вы собираетесь закончить тем, что имели два метода с тем же типом параметра.
Перегрузка походит на лучший способ первоначально, но если Вы заканчиваете тем, что не были способны добавлять метод в будущем, и портить вещи с именованием это будет стычкой.
Лично я был бы для для подхода уникального имени на метод, тот способ, которым Вы не сталкиваетесь с проблемами позже с попыткой перегрузить те же методы объекта параметра. Кроме того, если бы кто-то расширил Ваш класс в будущем и реализовал другую пустоту getEmployeeName (Имя строки), то это не переопределило бы Ваш.
Подводя итоги, пойдите с названием уникального метода каждого метода, перегрузка может только вызвать проблемы в конечном счете.
При перезаписи вопроса, можно закончить тем, что спросили:
"ВЫБЕРИТЕ имя ИЗ..."
"ВЫБЕРИТЕ SSN ИЗ..."
"ВЫБЕРИТЕ электронную почту ИЗ..."
по сравнению с.
"ВЫБЕРИТЕ * ИЗ..."
И я предполагаю, что ответ на это легок, и все знают это.
Что происходит, если Вы изменяете класс Сотрудника? Например: необходимо удалить электронную почту и добавить новый фильтр как отдел. Со вторым решением у Вас есть огромный риск не замечать любые ошибки, если Вы просто изменяете порядок международного идентификатора "константы". С первым решением Вы будете всегда замечать, используете ли Вы метод в некоторых давно забытых классах, Вы иначе забыли бы изменять к новому идентификатору.
Поскольку другие предположили, что первая опция, кажется, хорошая. Второе могло бы иметь смысл при написании кода, но когда кто-то еще приезжает позже, более трудно выяснить, как использовать код. (Я знаю, у Вас есть комментарии, и можно всегда рыть глубоко в код, но GetemployeeNameById более очевиден),
Примечание: Btw, использование Перечислений могло бы быть чем-то для рассмотрения в некоторых случаях.
Я использовал бы первую опцию или перегрузил бы ее в этом случае, видя, поскольку у Вас есть 4 различных подписи параметра. Однако быть конкретным помогает с пониманием кода 3 месяцам с этого времени.
Иногда может быть более удобно использовать шаблон спецификации.
Например: GetEmployee (ISpecification <Сотрудник> спецификация)
И затем начните определять свои спецификации...
NameSpecification: ISpecification <сотрудник>
{
частное имя строки;
общедоступный NameSpecification (имя строки) {this.name = имя;}
общедоступный bool IsSatisFiedBy (Сотрудник сотрудника) {возвращает сотрудника. Имя == this.name;}
}
Спецификация NameSpecification = новый NameSpecification ("Tim");
Сотрудник tim = MyService. GetEmployee (спецификация);
В тривиальном случае как это я пошел бы с перегрузкой. Это:
getEmployeeName( int batchID );
getEmployeeName( Object SSN );
etc.
Только в особых случаях был бы я указывать тип аргумента в имени метода, т.е. если тип аргумента трудно определить, если существует несколько типов аргументов tha, имеет совпадающий тип данных (batchId и employeeId, оба интервала), или если методы для получения сотрудника радикально отличаются для каждого типа аргумента.
Я не вижу, почему я когда-либо использовал бы это
getEmployeeName(int typeOfIdentifier, byte[] identifier)
поскольку это требует, чтобы и вызываемый и вызывающая сторона бросили значение на основе typeOfIdentifier. Плохой дизайн.
Я буду использовать явные имена методов. Все, которые поддерживают тот код и меня позже, поймут то, что тот метод обходится без необходимости записать комментариям xml.
@Stephan: трудно перегрузить случай как это (в целом), потому что типы параметра не могли бы быть отличительными, например,
См. также эти два метода getEmployeeNameBySSN, getEmployeeNameByEmailId в исходной регистрации.
Первая опция, никакой вопрос. Будьте явными. Это значительно поможет в пригодности для обслуживания и нет действительно никакой оборотной стороны.
Методы являются идеальным примером для использования перегрузки.
getEmployeeName(int batchID)
getEmployeeName(Object SSN)
getEmployeeName(String emailID)
getEmployeeName(SalaryAccount salaryAccount)
Если методы имеют общую обработку внутри, просто запишите еще один getEmplyeeNameImpl (...) и извлеките там общий код для предотвращения дублирования
Мне не нравится getXByY () - который мог бы быть прохладным в PHP, но мне просто не нравится он в Java (ymmv).
Я пошел бы с перегрузкой, если у Вас нет свойств того же типа данных. В этом случае я сделал бы что-то подобное Вашей второй опции, но вместо того, чтобы использовать ints, я буду использовать Перечисление для безопасности типов и ясности. И вместо байта [], я использовал бы Объект (из-за автоупаковки, это также работает на примитивы).
Я пошел бы с подходом "многих". Это кажется более интуитивным мне и менее подверженным ошибке.
Вы могли использовать что-то как этот:
interface Employee{
public String getName();
int getBatchId();
}
interface Filter{
boolean matches(Employee e);
}
public Filter byName(final String name){
return new Filter(){
public boolean matches(Employee e) {
return e.getName().equals(name);
}
};
}
public Filter byBatchId(final int id){
return new Filter(){
public boolean matches(Employee e) {
return e.getBatchId() == id;
}
};
}
public Employee findEmployee(Filter sel){
List<Employee> allEmployees = null;
for (Employee e:allEmployees)
if (sel.matches(e))
return e;
return null;
}
public void usage(){
findEmployee(byName("Gustav"));
findEmployee(byBatchId(5));
}
Если бы Вы делаете фильтрацию по SQL-запросу, Вы использовали бы Filter
интерфейс для создания оператора Where.
Хорошая вещь с этим подходом состоит в том, что можно объединить два фильтра легко с:
public Filter and(final Filter f1,final Filter f2){
return new Filter(){
public boolean matches(Employee e) {
return f1.matches(e) && f2.matches(e);
}
};
}
и используйте его как этот:
findEmployee(and(byName("Gustav"),byBatchId(5)));
То, что Вы получаете, подобно Criteria
API в в спящем режиме.
Является логика в каждом из тех методов в основном тем же?
Если так, отдельный метод с параметром идентификатора может иметь больше смысла (простой и уменьшающий повторный код).
Если логика/процедуры варьируется значительно между типами, метод на тип может быть предпочтен.