О некоторых частях ваших вопросов (из года в год):
1.Если вы имеете в виду, что значение по умолчанию было бы настолько умным, и вам не понадобилось бы его менять в будущем, тогда ваш желание ошибочно. почему?
по двум причинам:
2. Он может обрабатывать код, который я вам покажу.
Поэтому, основываясь на этих объяснениях, у вас может быть столбец со значением по умолчанию, который существует в Fkeys, но это не будет чем-то, что вам нужно, и вы должны обновить его в будущей базе при использовании. и для этого вам нужно:
FIRST:
CREATE FUNCTION SelectMinForeignKey () RETURNS INT AS BEGIN DECLARE @FirstID INT SELECT @FirstID = MIN (ID) из DetailTableExample01 RETURN @FirstID END
SECOND:
ALTER TABLE example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey ()
ALTER TABLE example1 ADD NoNullableCol2 INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey (), FOREIGN KEY (NoNullableCol2) ССЫЛКИ DetailTableExample01 (id);
ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey (), CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) СПИСОК ЛИТЕРАТУРЫ dbo.DetailTableExample01 (ID) В ОБНОВЛЕНИИ CASCADE ON DELETE CASCADE;
ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey () GO ALTER ТАБЛИЦА dbo.example1 ADD CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) ССЫЛКИ dbo.DetailTableExample01 (ID) В ОБНОВЛЕНИИ CASCADE ON DELETE CASCADE
ПРИМЕЧАНИЕ. Как вы знаете, имена таблиц и столбцов являются образцами.
THIRD:
CREATE FUNCTION SelectMinForeignKey () RETURNS INT AS BEGIN DECLARE @FirstID INT SELECT @FirstID = MIN (ID) из DetailTableExample01 RETURN @FirstID END GO ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey (), CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) ССЫЛКИ dbo.DetailTableExample01 (ID) ВКЛ. ОБНОВЛЕНИЕ КАСКАДЫ НА УДАЛИТЬ КАСКАД;
И все готово!
Надеюсь, он решает вашу проблему
Вы можете сделать это, используя функцию Select
из пакета reflect :
func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
Select выполняет операцию выбора описанных в списке случаев. Подобно оператору выбора Go, он блокируется, пока, по крайней мере, один из случаев не может продолжить, делает равномерный псевдослучайный выбор, а затем выполняет этот случай. Он возвращает индекс выбранного случая и, если в этом случае была операция приема, полученное значение и логическое значение указывают, соответствует ли значение отправке по каналу (в отличие от нулевого значения, полученного, поскольку канал закрыт).
blockquote>Вы передаете массив структур
SelectCase
, которые определяют канал для выбора, направление операции и значение для отправки в случае операции отправки.Итак, вы можете сделать что-то вроде этого:
cases := make([]reflect.SelectCase, len(chans)) for i, ch := range chans { cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)} } chosen, value, ok := reflect.Select(cases) # ok will be true if the channel has not been closed. ch := chans[chosen] msg := value.String()
Вы можете поэкспериментировать с более сложным примером здесь: http://play.golang.org/p/8zwvSk4kjx
Чтобы расширить некоторые комментарии к предыдущим ответам и дать более четкое сравнение, приведен пример обоих подходов, представленных до сих пор, с учетом того же ввода, фрагмента каналов для чтения и функции для вызова каждого значения, которое также необходимо чтобы узнать, к какому каналу пришло значение.
Существуют три основных различия между подходами:
Обратите внимание, что оба подхода могут быть упрощены, если «id» передающего канала не требуется или если исходные каналы никогда не будут закрыты.
Канал объединения Goroutine:
// Process1 calls `fn` for each value received from any of the `chans`
// channels. The arguments to `fn` are the index of the channel the
// value came from and the string value. Process1 returns once all the
// channels are closed.
func Process1(chans []<-chan string, fn func(int, string)) {
// Setup
type item struct {
int // index of which channel this came from
string // the actual string item
}
merged := make(chan item)
var wg sync.WaitGroup
wg.Add(len(chans))
for i, c := range chans {
go func(i int, c <-chan string) {
// Reads and buffers a single item from `c` before
// we even know if we can write to `merged`.
//
// Go doesn't provide a way to do something like:
// merged <- (<-c)
// atomically, where we delay the read from `c`
// until we can write to `merged`. The read from
// `c` will always happen first (blocking as
// required) and then we block on `merged` (with
// either the above or the below syntax making
// no difference).
for s := range c {
merged <- item{i, s}
}
// If/when this input channel is closed we just stop
// writing to the merged channel and via the WaitGroup
// let it be known there is one fewer channel active.
wg.Done()
}(i, c)
}
// One extra goroutine to watch for all the merging goroutines to
// be finished and then close the merged channel.
go func() {
wg.Wait()
close(merged)
}()
// "select-like" loop
for i := range merged {
// Process each value
fn(i.int, i.string)
}
}
Выбор выделения:
// Process2 is identical to Process1 except that it uses the reflect
// package to select and read from the input channels which guarantees
// there is only one value "in-flight" (i.e. when `fn` is called only
// a single send on a single channel will have succeeded, the rest will
// be blocked). It is approximately two orders of magnitude slower than
// Process1 (which is still insignificant if their is a significant
// delay between incoming values or if `fn` runs for a significant
// time).
func Process2(chans []<-chan string, fn func(int, string)) {
// Setup
cases := make([]reflect.SelectCase, len(chans))
// `ids` maps the index within cases to the original `chans` index.
ids := make([]int, len(chans))
for i, c := range chans {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(c),
}
ids[i] = i
}
// Select loop
for len(cases) > 0 {
// A difference here from the merging goroutines is
// that `v` is the only value "in-flight" that any of
// the workers have sent. All other workers are blocked
// trying to send the single value they have calculated
// where-as the goroutine version reads/buffers a single
// extra value from each worker.
i, v, ok := reflect.Select(cases)
if !ok {
// Channel cases[i] has been closed, remove it
// from our slice of cases and update our ids
// mapping as well.
cases = append(cases[:i], cases[i+1:]...)
ids = append(ids[:i], ids[i+1:]...)
continue
}
// Process each value
fn(ids[i], v.String())
}
}
[Полный код на игровой площадке Go .]
select
или reflect.Select
. Горотины будут продолжать вращаться, пока они не будут потреблять все из каналов, поэтому нет четкого пути, по которому вы могли бы Process1
выйти раньше. Существует также проблема для проблем, если у вас несколько читателей, поскольку goroutines задерживают один элемент из каждого из каналов, чего не будет с select
.
– James Henstridge
4 November 2015 в 04:44
select
в цикле for
вместо более простого for range
в настоящее время. Process2 нужно было бы вставить еще один случай в cases
и специальный дескриптор этого значения i
.
– Dave C
7 January 2016 в 21:42
Почему этот подход не работает, если кто-то отправляет события?
func main() {
numChans := 2
var chans = []chan string{}
for i := 0; i < numChans; i++ {
tmp := make(chan string)
chans = append(chans, tmp)
}
for true {
for i, c := range chans {
select {
case x = <-c:
fmt.Printf("received %d \n", i)
go DoShit(x, i)
default: continue
}
}
}
}
select
на нескольких каналах (без предложения default
) заключается в том, что она эффективно ждет, пока, по крайней мере, один не будет готов к вращению.
– Dave C
29 June 2018 в 14:18
Вы можете выполнить это, обернув каждый канал в goroutine, который «пересылает» сообщения на общий «совокупный» канал. Например:
agg := make(chan string)
for _, ch := range chans {
go func(c chan string) {
for msg := range c {
agg <- msg
}
}(ch)
}
select {
case msg <- agg:
fmt.Println("received ", msg)
}
Если вам нужно знать, с какого канала отправлено сообщение, вы можете перенести его в структуру с какой-либо дополнительной информацией, прежде чем перенаправлять ее на общий канал.
В моем (ограниченном) тестировании этот метод значительно отличается от используемого пакета отражения:
$ go test dynamic_select_test.go -test.bench=.
...
BenchmarkReflectSelect 1 5265109013 ns/op
BenchmarkGoSelect 20 81911344 ns/op
ok command-line-arguments 9.463s
Код контрольной точки здесь
b.N
в рамках теста. В противном случае результаты (которые делятся на b.N
, 1 и 2000000000 в вашем выходе) будут совершенно бессмысленными.
– Dave C
2 September 2015 в 16:27
reflect.Select
) является то, что goroutines выполняют слияние буфера как минимум на одном значении на каждом канале, который объединяется. Обычно это не будет проблемой, но в некоторых конкретных приложениях, которые могут быть прерывателем сделки :(.
– Dave C
2 September 2015 в 17:36